diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000000..23c94155f3fd3 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,121 @@ +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +FROM mcr.microsoft.com/vscode/devcontainers/typescript-node:0-12 + +ARG TARGET_DISPLAY=":1" + +# VNC options +ARG MAX_VNC_RESOLUTION=1920x1080x16 +ARG TARGET_VNC_RESOLUTION=1920x1080 +ARG TARGET_VNC_DPI=72 +ARG TARGET_VNC_PORT=5901 +ARG VNC_PASSWORD="vscode" + +# noVNC (VNC web client) options +ARG INSTALL_NOVNC="true" +ARG NOVNC_VERSION=1.1.0 +ARG TARGET_NOVNC_PORT=6080 +ARG WEBSOCKETIFY_VERSION=0.9.0 + +# Firefox is useful for testing things like browser launch events, but optional +ARG INSTALL_FIREFOX="false" + +# Expected non-root username from base image +ARG USERNAME=node + +# Core environment variables for X11, VNC, and fluxbox +ENV DBUS_SESSION_BUS_ADDRESS="autolaunch:" \ + MAX_VNC_RESOLUTION="${MAX_VNC_RESOLUTION}" \ + VNC_RESOLUTION="${TARGET_VNC_RESOLUTION}" \ + VNC_DPI="${TARGET_VNC_DPI}" \ + VNC_PORT="${TARGET_VNC_PORT}" \ + NOVNC_PORT="${TARGET_NOVNC_PORT}" \ + DISPLAY="${TARGET_DISPLAY}" \ + LANG="en_US.UTF-8" \ + LANGUAGE="en_US.UTF-8" \ + VISUAL="nano" \ + EDITOR="nano" + +# Configure apt and install packages +RUN apt-get update \ + && export DEBIAN_FRONTEND=noninteractive \ + # + # Install the Cascadia Code fonts - https://github.com/microsoft/cascadia-code + && curl -sSL https://github.com/microsoft/cascadia-code/releases/download/v2004.30/CascadiaCode_2004.30.zip -o /tmp/cascadia-fonts.zip \ + && unzip /tmp/cascadia-fonts.zip -d /tmp/cascadia-fonts \ + && mkdir -p /usr/share/fonts/truetype/cascadia \ + && mv /tmp/cascadia-fonts/ttf/* /usr/share/fonts/truetype/cascadia/ \ + && rm -rf /tmp/cascadia-fonts.zip /tmp/cascadia-fonts \ + # + # Install X11, fluxbox and VS Code dependencies + && apt-get -y install --no-install-recommends \ + xvfb \ + x11vnc \ + fluxbox \ + dbus-x11 \ + x11-utils \ + x11-xserver-utils \ + xdg-utils \ + fbautostart \ + xterm \ + eterm \ + gnome-terminal \ + gnome-keyring \ + seahorse \ + nautilus \ + libx11-dev \ + libxkbfile-dev \ + libsecret-1-dev \ + libnotify4 \ + libnss3 \ + libxss1 \ + libasound2 \ + xfonts-base \ + xfonts-terminus \ + fonts-noto \ + fonts-wqy-microhei \ + fonts-droid-fallback \ + vim-tiny \ + nano \ + # + # [Optional] Install noVNC + && if [ "${INSTALL_NOVNC}" = "true" ]; then \ + mkdir -p /usr/local/novnc \ + && curl -sSL https://github.com/novnc/noVNC/archive/v${NOVNC_VERSION}.zip -o /tmp/novnc-install.zip \ + && unzip /tmp/novnc-install.zip -d /usr/local/novnc \ + && cp /usr/local/novnc/noVNC-${NOVNC_VERSION}/vnc_lite.html /usr/local/novnc/noVNC-${NOVNC_VERSION}/index.html \ + && rm /tmp/novnc-install.zip \ + && curl -sSL https://github.com/novnc/websockify/archive/v${WEBSOCKETIFY_VERSION}.zip -o /tmp/websockify-install.zip \ + && unzip /tmp/websockify-install.zip -d /usr/local/novnc \ + && apt-get -y install --no-install-recommends python-numpy \ + && ln -s /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION} /usr/local/novnc/noVNC-${NOVNC_VERSION}/utils/websockify \ + && rm /tmp/websockify-install.zip; \ + fi \ + # + # [Optional] Install Firefox + && if [ "${INSTALL_FIREFOX}" = "true" ]; then \ + apt-get -y install --no-install-recommends firefox-esr; \ + fi \ + # + # Clean up + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + +COPY bin/init-dev-container.sh /usr/local/share/ +COPY bin/set-resolution /usr/local/bin/ +COPY fluxbox/* /root/.fluxbox/ +COPY fluxbox/* /home/${USERNAME}/.fluxbox/ + +# Update privs, owners of config files +RUN mkdir -p /var/run/dbus /root/.vnc /home/${USERNAME}/.vnc \ + && touch /root/.Xmodmap /home/${USERNAME}/.Xmodmap \ + && echo "${VNC_PASSWORD}" | tee /root/.vnc/passwd > /home/${USERNAME}/.vnc/passwd \ + && chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/.Xmodmap /home/${USERNAME}/.fluxbox /home/${USERNAME}/.vnc \ + && chmod +x /usr/local/share/init-dev-container.sh /usr/local/bin/set-resolution + +ENTRYPOINT ["/usr/local/share/init-dev-container.sh"] +CMD ["sleep", "infinity"] diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000000000..e16795062d7ea --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,82 @@ +# Code - OSS Development Container + +This repository includes configuration for a development container for working with Code - OSS in an isolated local container or using [Visual Studio Codespaces](https://aka.ms/vso). + +> **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. [Chicken](https://sourceforge.net/projects/chicken/) is a good macOS alternative. + +## 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: Open Repository in Container...**. + + > **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 "open repository" 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 - Codespaces + +>Note that the Codespaces browser-based editor cannot currently access the desktop environment in this container (due to a [missing feature](https://github.com/MicrosoftDocs/vsonline/issues/117)). We recommend using Visual Studio Code from the desktop to connect instead in the near term. + +1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [Visual Studio Codespaces](https://aka.ms/vscs-ext-vscode) extension. + + ![Image of VS Codespaces extension](https://microsoft.github.io/vscode-remote-release/images/codespaces-extn.png) + + > Note that the Visual Studio Codespaces extension requires the Visual Studio Code distribution of Code - OSS. + +2. Sign in by pressing Ctrl/Cmd + Shift + P and selecting **Codespaces: Sign In**. You may also need to use the **Codespaces: Create Plan** if you do not have a plan. See the [Codespaces docs](https://aka.ms/vso-docs/vscode) for details. + +3. Press Ctrl/Cmd + Shift + P and select **Codespaces: Create New Codespace**. + +4. Use default settings (which should include **Standard** 4 core, 8 GB RAM Codespace), select a plan, and then enter the repository URL `https://github.com/microsoft/vscode` (or a branch or PR URL) in the input box when prompted. + +5. 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. + +6. Anything you start in VS Code or the integrated terminal will appear here. + +## 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 + ``` + +2. After the build is complete, 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. + +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/bin/init-dev-container.sh b/.devcontainer/bin/init-dev-container.sh new file mode 100644 index 0000000000000..260cc27592243 --- /dev/null +++ b/.devcontainer/bin/init-dev-container.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +NONROOT_USER=node +LOG=/tmp/container-init.log + +# Execute the command it not already running +startInBackgroundIfNotRunning() +{ + log "Starting $1." + echo -e "\n** $(date) **" | sudoIf tee -a /tmp/$1.log > /dev/null + if ! pidof $1 > /dev/null; then + keepRunningInBackground "$@" + while ! pidof $1 > /dev/null; do + sleep 1 + done + log "$1 started." + else + echo "$1 is already running." | sudoIf tee -a /tmp/$1.log > /dev/null + log "$1 is already running." + fi +} + +# Keep command running in background +keepRunningInBackground() +{ + ($2 sh -c "while :; do echo [\$(date)] Process started.; $3; echo [\$(date)] Process exited!; sleep 5; done 2>&1" | sudoIf tee -a /tmp/$1.log > /dev/null & echo "$!" | sudoIf tee /tmp/$1.pid > /dev/null) +} + +# Use sudo to run as root when required +sudoIf() +{ + if [ "$(id -u)" -ne 0 ]; then + sudo "$@" + else + "$@" + fi +} + +# Use sudo to run as non-root user if not already running +sudoUserIf() +{ + if [ "$(id -u)" -eq 0 ]; then + sudo -u ${NONROOT_USER} "$@" + else + "$@" + fi +} + +# Log messages +log() +{ + echo -e "[$(date)] $@" | sudoIf tee -a $LOG > /dev/null +} + +log "** SCRIPT START **" + +# Start dbus. +log 'Running "/etc/init.d/dbus start".' +if [ -f "/var/run/dbus/pid" ] && ! pidof dbus-daemon > /dev/null; then + sudoIf rm -f /var/run/dbus/pid +fi +sudoIf /etc/init.d/dbus start 2>&1 | sudoIf tee -a /tmp/dbus-daemon-system.log > /dev/null +while ! pidof dbus-daemon > /dev/null; do + sleep 1 +done + +# Set up Xvfb. +startInBackgroundIfNotRunning "Xvfb" sudoIf "Xvfb ${DISPLAY:-:1} +extension RANDR -screen 0 ${MAX_VNC_RESOLUTION:-1920x1080x16}" + +# Start fluxbox as a light weight window manager. +startInBackgroundIfNotRunning "fluxbox" sudoUserIf "dbus-launch startfluxbox" + +# Start x11vnc +startInBackgroundIfNotRunning "x11vnc" sudoIf "x11vnc -display ${DISPLAY:-:1} -rfbport ${VNC_PORT:-5901} -localhost -no6 -xkb -shared -forever -passwdfile $HOME/.vnc/passwd" + +# Set resolution +/usr/local/bin/set-resolution ${VNC_RESOLUTION:-1280x720} ${VNC_DPI:-72} + + +# Spin up noVNC if installed and not runnning. +if [ -d "/usr/local/novnc" ] && [ "$(ps -ef | grep /usr/local/novnc/noVNC*/utils/launch.sh | grep -v grep)" = "" ]; then + keepRunningInBackground "noVNC" sudoIf "/usr/local/novnc/noVNC*/utils/launch.sh --listen ${NOVNC_PORT:-6080} --vnc localhost:${VNC_PORT:-5901}" + log "noVNC started." +else + log "noVNC is already running or not installed." +fi + +# Run whatever was passed in +log "Executing \"$@\"." +"$@" +log "** SCRIPT EXIT **" diff --git a/.devcontainer/bin/set-resolution b/.devcontainer/bin/set-resolution new file mode 100644 index 0000000000000..5b4ca79f51883 --- /dev/null +++ b/.devcontainer/bin/set-resolution @@ -0,0 +1,25 @@ +#!/bin/bash +RESOLUTION=${1:-${VNC_RESOLUTION:-1920x1080}} +DPI=${2:-${VNC_DPI:-72}} +if [ -z "$1" ]; then + echo -e "**Current Settings **\n" + xrandr + echo -n -e "\nEnter new resolution (WIDTHxHEIGHT, blank for ${RESOLUTION}, Ctrl+C to abort).\n> " + read NEW_RES + if [ "${NEW_RES}" != "" ]; then + RESOLUTION=${NEW_RES} + fi + if [ -z "$2" ]; then + echo -n -e "\nEnter new DPI (blank for ${DPI}, Ctrl+C to abort).\n> " + read NEW_DPI + if [ "${NEW_DPI}" != "" ]; then + DPI=${NEW_DPI} + fi + fi +fi + +xrandr --fb ${RESOLUTION} --dpi ${DPI} > /dev/null 2>&1 + +echo -e "\n**New Settings **\n" +xrandr +echo diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000000..722bace6df74d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,45 @@ +{ + "name": "Code - OSS", + "build": { + "dockerfile": "Dockerfile", + "args": { + "MAX_VNC_RESOLUTION": "1920x1080x16", + "TARGET_VNC_RESOLUTION": "1280x768", + "TARGET_VNC_PORT": "5901", + "TARGET_NOVNC_PORT": "6080", + "VNC_PASSWORD": "vscode", + "INSTALL_FIREFOX": "true" + } + }, + "overrideCommand": false, + "runArgs": [ + "--init", + // seccomp=unconfined is required for Chrome sandboxing + "--security-opt", "seccomp=unconfined" + ], + + "settings": { + // zsh is also available + "terminal.integrated.shell.linux": "/bin/bash", + "resmon.show.battery": false, + "resmon.show.cpufreq": false, + "remote.extensionKind": { + "ms-vscode.js-debug-nightly": "workspace", + "msjsdiag.debugger-for-chrome": "workspace" + }, + "debug.chrome.useV3": true + }, + + // noVNC, VNC ports + "forwardPorts": [6080, 5901], + + "extensions": [ + "dbaeumer.vscode-eslint", + "EditorConfig.EditorConfig", + "msjsdiag.debugger-for-chrome", + "mutantdino.resourcemonitor", + "GitHub.vscode-pull-request-github" + ], + + "remoteUser": "node" +} diff --git a/.devcontainer/fluxbox/apps b/.devcontainer/fluxbox/apps new file mode 100644 index 0000000000000..d43f05e9e2057 --- /dev/null +++ b/.devcontainer/fluxbox/apps @@ -0,0 +1,9 @@ +[app] (name=code-oss-dev) + [Position] (CENTER) {0 0} + [Maximized] {yes} + [Dimensions] {100% 100%} +[end] +[transient] (role=GtkFileChooserDialog) + [Position] (CENTER) {0 0} + [Dimensions] {70% 70%} +[end] diff --git a/.devcontainer/fluxbox/init b/.devcontainer/fluxbox/init new file mode 100644 index 0000000000000..a6b8d73fa73d4 --- /dev/null +++ b/.devcontainer/fluxbox/init @@ -0,0 +1,9 @@ +session.menuFile: ~/.fluxbox/menu +session.keyFile: ~/.fluxbox/keys +session.styleFile: /usr/share/fluxbox/styles//Squared_for_Debian +session.configVersion: 13 +session.screen0.workspaces: 1 +session.screen0.workspacewarping: false +session.screen0.toolbar.widthPercent: 100 +session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S +session.screen0.toolbar.tools: prevworkspace, workspacename, nextworkspace, clock, prevwindow, nextwindow, iconbar, systemtray diff --git a/.devcontainer/fluxbox/menu b/.devcontainer/fluxbox/menu new file mode 100644 index 0000000000000..ff5955a2fe75a --- /dev/null +++ b/.devcontainer/fluxbox/menu @@ -0,0 +1,16 @@ +[begin] ( Code - OSS Development Container ) + [exec] (File Manager) { nautilus ~ } <> + [exec] (Terminal) {/usr/bin/gnome-terminal --working-directory=~ } <> + [exec] (Start Code - OSS) { x-terminal-emulator -T "Code - OSS Build" -e bash /workspaces/vscode*/scripts/code.sh } <> + [submenu] (System >) {} + [exec] (Set Resolution) { x-terminal-emulator -T "Set Resolution" -e bash /usr/local/bin/set-resolution } <> + [exec] (Passwords and Keys) { seahorse } <> + [exec] (Top) { x-terminal-emulator -T "Top" -e /usr/bin/top } <> + [exec] (Editres) {editres} <> + [exec] (Xfontsel) {xfontsel} <> + [exec] (Xkill) {xkill} <> + [exec] (Xrefresh) {xrefresh} <> + [end] + [config] (Configuration >) + [workspaces] (Workspaces >) +[end] diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000000..f186c7ecd78d8 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,15 @@ +**/vs/nls.build.js +**/vs/nls.js +**/vs/css.build.js +**/vs/css.js +**/vs/loader.js +**/insane/** +**/marked/** +**/test/**/*.js +**/node_modules/** +**/vscode-api-tests/testWorkspace/** +**/vscode-api-tests/testWorkspace2/** +**/extensions/**/out/** +**/extensions/**/build/** +**/extensions/markdown-language-features/media/** +**/extensions/typescript-basics/test/colorize-fixtures/** diff --git a/.eslintrc.json b/.eslintrc.json index 29efa7cbbc202..df3c5ad560c4b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,20 +1,994 @@ { - "root": true, - "env": { - "node": true, - "es6": true - }, - "rules": { - "no-console": 0, - "no-cond-assign": 0, - "no-unused-vars": 1, - "no-extra-semi": "warn", - "semi": "warn" - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaFeatures": { - "experimentalObjectRestSpread": true - } - } + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint", + "jsdoc" + ], + "rules": { + "constructor-super": "warn", + "curly": "warn", + "eqeqeq": "warn", + "no-buffer-constructor": "warn", + "no-caller": "warn", + "no-debugger": "warn", + "no-duplicate-case": "warn", + "no-duplicate-imports": "warn", + "no-eval": "warn", + "no-extra-semi": "warn", + "no-new-wrappers": "warn", + "no-redeclare": "off", + "no-sparse-arrays": "warn", + "no-throw-literal": "warn", + "no-unsafe-finally": "warn", + "no-unused-labels": "warn", + "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", + "@typescript-eslint/semi": "warn", + "@typescript-eslint/naming-convention": [ + "warn", + { + "selector": "class", + "format": [ + "PascalCase" + ] + } + ], + "code-no-unused-expressions": [ + "warn", + { + "allowTernary": true + } + ], + "code-translation-remind": "warn", + "code-no-nls-in-standalone-editor": "warn", + "code-no-standalone-editor": "warn", + "code-no-unexternalized-strings": "warn", + "code-layering": [ + "warn", + { + "common": [], + "node": [ + "common" + ], + "browser": [ + "common" + ], + "electron-sandbox": [ + "common", + "browser" + ], + "electron-browser": [ + "common", + "browser", + "node", + "electron-sandbox" + ], + "electron-main": [ + "common", + "node" + ] + } + ], + "code-import-patterns": [ + "warn", + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // !!! Do not relax these rules !!! + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + { + "target": "**/vs/base/common/**", + "restrictions": [ + "vs/nls", + "**/vs/base/common/**" + ] + }, + { + "target": "**/vs/base/test/common/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/common/**", + "**/vs/base/test/common/**" + ] + }, + { + "target": "**/vs/base/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/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}/**", + "*" // node modules + ] + }, + { + // vs/base/test/browser contains tests for vs/base/browser + "target": "**/vs/base/test/browser/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/{common,browser}/**", + "**/vs/base/test/{common,browser}/**" + ] + }, + { + "target": "**/vs/base/parts/*/common/**", + "restrictions": [ + "vs/nls", + "**/vs/base/common/**", + "**/vs/base/parts/*/common/**" + ] + }, + { + "target": "**/vs/base/parts/*/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**" + ] + }, + { + "target": "**/vs/base/parts/*/node/**", + "restrictions": [ + "vs/nls", + "**/vs/base/{common,node}/**", + "**/vs/base/parts/*/{common,node}/**", + "*" // 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}/**" + ] + }, + { + "target": "**/vs/base/parts/*/electron-browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", + "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", + "*" // node modules + ] + }, + { + "target": "**/vs/base/parts/*/electron-main/**", + "restrictions": [ + "vs/nls", + "**/vs/base/{common,node,electron-main}/**", + "**/vs/base/parts/*/{common,node,electron-main}/**", + "*" // node modules + ] + }, + { + "target": "**/vs/platform/*/common/**", + "restrictions": [ + "vs/nls", + "**/vs/base/common/**", + "**/vs/base/parts/*/common/**", + "**/vs/platform/*/common/**" + ] + }, + { + "target": "**/vs/platform/*/test/common/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/common/**", + "**/vs/base/parts/*/common/**", + "**/vs/base/test/common/**", + "**/vs/platform/*/common/**", + "**/vs/platform/*/test/common/**" + ] + }, + { + "target": "**/vs/platform/*/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**" + ] + }, + { + "target": "**/vs/platform/*/node/**", + "restrictions": [ + "vs/nls", + "**/vs/base/{common,node}/**", + "**/vs/base/parts/*/{common,node}/**", + "**/vs/platform/*/{common,node}/**", + "*" // 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}/**" + ] + }, + { + "target": "**/vs/platform/*/electron-browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/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 + ] + }, + { + "target": "**/vs/platform/*/electron-main/**", + "restrictions": [ + "vs/nls", + "**/vs/base/{common,node,electron-main}/**", + "**/vs/base/parts/*/{common,node,electron-main}/**", + "**/vs/platform/*/{common,node,electron-main}/**", + "**/vs/code/**", + "*" // node modules + ] + }, + { + "target": "**/vs/platform/*/test/browser/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/platform/*/test/{common,browser}/**" + ] + }, + { + "target": "**/vs/editor/common/**", + "restrictions": [ + "vs/nls", + "**/vs/base/common/**", + "**/vs/base/worker/**", + "**/vs/platform/*/common/**", + "**/vs/editor/common/**" + ] + }, + { + "target": "**/vs/editor/test/common/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/common/**", + "**/vs/platform/*/common/**", + "**/vs/platform/*/test/common/**", + "**/vs/editor/common/**", + "**/vs/editor/test/common/**" + ] + }, + { + "target": "**/vs/editor/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/editor/{common,browser}/**" + ] + }, + { + "target": "**/vs/editor/test/browser/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/platform/*/test/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/editor/test/{common,browser}/**" + ] + }, + { + "target": "**/vs/editor/standalone/common/**", + "restrictions": [ + "vs/nls", + "**/vs/base/common/**", + "**/vs/platform/*/common/**", + "**/vs/editor/common/**", + "**/vs/editor/standalone/common/**" + ] + }, + { + "target": "**/vs/editor/standalone/test/common/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/common/**", + "**/vs/platform/*/common/**", + "**/vs/platform/*/test/common/**", + "**/vs/editor/common/**", + "**/vs/editor/test/common/**" + ] + }, + { + "target": "**/vs/editor/standalone/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/editor/contrib/**", + "**/vs/editor/standalone/{common,browser}/**" + ] + }, + { + "target": "**/vs/editor/standalone/test/browser/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/platform/*/test/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/editor/standalone/{common,browser}/**", + "**/vs/editor/test/{common,browser}/**" + ] + }, + { + "target": "**/vs/editor/contrib/*/test/**", + "restrictions": [ + "assert", + "sinon", + "vs/nls", + "**/vs/base/{common,browser}/**", + "**/vs/base/test/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/platform/*/test/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/editor/test/{common,browser}/**", + "**/vs/editor/contrib/**" + ] + }, + { + "target": "**/vs/editor/contrib/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**", + "**/vs/platform/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/editor/contrib/**" + ] + }, + { + "target": "**/vs/workbench/common/**", + "restrictions": [ + "vs/nls", + "**/vs/base/common/**", + "**/vs/base/parts/*/common/**", + "**/vs/platform/*/common/**", + "**/vs/editor/common/**", + "**/vs/editor/contrib/*/common/**", + "**/vs/workbench/common/**", + "**/vs/workbench/services/*/common/**", + "assert" + ] + }, + { + "target": "**/vs/workbench/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention + "**/vs/workbench/workbench.web.api", + "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/services/*/{common,browser}/**", + "assert" + ] + }, + { + "target": "**/vs/workbench/api/common/**", + "restrictions": [ + "vscode", + "vs/nls", + "**/vs/base/common/**", + "**/vs/platform/*/common/**", + "**/vs/editor/common/**", + "**/vs/editor/contrib/*/common/**", + "**/vs/workbench/api/common/**", + "**/vs/workbench/common/**", + "**/vs/workbench/services/*/common/**", + "**/vs/workbench/contrib/*/common/**" + ] + }, + { + "target": "**/vs/workbench/api/worker/**", + "restrictions": [ + "vscode", + "vs/nls", + "**/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-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-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/services/**/test/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**", + "**/vs/platform/**", + "**/vs/editor/**", + "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", + "vs/workbench/contrib/files/common/editors/fileEditorInput", + "**/vs/workbench/services/**", + "**/vs/workbench/test/**", + "*" // node modules + ] + }, + { + "target": "**/vs/workbench/services/**/common/**", + "restrictions": [ + "vs/nls", + "**/vs/base/**/common/**", + "**/vs/platform/**/common/**", + "**/vs/editor/common/**", + "**/vs/workbench/workbench.web.api", + "**/vs/workbench/common/**", + "**/vs/workbench/services/**/common/**", + "**/vs/workbench/api/**/common/**", + "vscode-textmate", + "vscode-oniguruma", + "iconv-lite-umd", + "semver-umd" + ] + }, + { + "target": "**/vs/workbench/services/**/worker/**", + "restrictions": [ + "vs/nls", + "**/vs/base/**/common/**", + "**/vs/platform/**/common/**", + "**/vs/editor/common/**", + "**/vs/workbench/**/common/**", + "**/vs/workbench/**/worker/**", + "**/vs/workbench/services/**/common/**", + "vscode" + ] + }, + { + "target": "**/vs/workbench/services/**/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/**/{common,browser,worker}/**", + "**/vs/platform/**/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/workbench/workbench.web.api", + "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/api/{common,browser}/**", + "**/vs/workbench/services/**/{common,browser}/**", + "vscode-textmate", + "vscode-oniguruma" + ] + }, + { + "target": "**/vs/workbench/services/**/node/**", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,node}/**", + "**/vs/platform/**/{common,node}/**", + "**/vs/editor/{common,node}/**", + "**/vs/workbench/{common,node}/**", + "**/vs/workbench/api/{common,node}/**", + "**/vs/workbench/services/**/{common,node}/**", + "*" // 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}/**" + ] + }, + { + "target": "**/vs/workbench/services/**/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}/**", + "*" // 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/**", + "*" + ] + }, + { + "target": "**/vs/workbench/contrib/terminal/browser/**", + "restrictions": [ + // xterm and its addons are strictly browser-only components + "xterm", + "xterm-addon-*", + "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/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/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}/**" + ] + }, + { + "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}/**" + ] + }, + { + "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" + ] + }, + { + "target": "**/vs/code/node/**", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,node}/**", + "**/vs/base/parts/**/{common,node}/**", + "**/vs/platform/**/{common,node}/**", + "**/vs/code/**/{common,node}/**", + "*" // node modules + ] + }, + { + "target": "**/vs/code/electron-browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/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 + ] + }, + { + "target": "**/vs/code/electron-main/**", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,node,electron-main}/**", + "**/vs/base/parts/**/{common,node,electron-main}/**", + "**/vs/platform/**/{common,node,electron-main}/**", + "**/vs/code/**/{common,node,electron-main}/**", + "*" // node modules + ] + }, + { + "target": "**/vs/server/**", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,node}/**", + "**/vs/base/parts/**/{common,node}/**", + "**/vs/platform/**/{common,node}/**", + "**/vs/workbench/**/{common,node}/**", + "**/vs/server/**", + "**/vs/code/**/{common,node}/**", + "*" // node modules + ] + }, + { + "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/**", + "restrictions": "**/*" + }, + { + "target": "**/test/smoke/**", + "restrictions": [ + "**/test/smoke/**", + "*" // node modules + ] + }, + { + "target": "**/test/automation/**", + "restrictions": [ + "**/test/automation/**", + "*" // node modules + ] + }, + { + "target": "**/test/integration/**", + "restrictions": [ + "**/test/integration/**", + "*" // node modules + ] + }, + { + "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*" + ] + } + ] + }, + "overrides": [ + { + "files": [ + "*.js" + ], + "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 addf36f4339e4..acb1cb3d9c61f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,8 @@ 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 a820760fc48f7..b9c6c83caa3d9 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,6 +4,8 @@ about: Suggest an idea for this project --- + + diff --git a/.github/classifier.json b/.github/classifier.json new file mode 100644 index 0000000000000..33c179d323763 --- /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": ["joaomoreno"], + "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 92da2b03f5fb1..0000000000000 --- 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: [ jrieken ], - 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 0000000000000..1b2bc516842d4 --- /dev/null +++ b/.github/commands.json @@ -0,0 +1,381 @@ +[ + { + "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": "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 0e78ac749fafa..64fdf683bfe99 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -1,127 +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'], - 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: 'comment', - name: 'a11ymas', - allowUsers: ['AccessibilityTestingTeam-TCS', 'dixitsonali95', 'Mohini78', 'ChitrarupaSharma', 'mspatil110', 'umasarath52', 'v-umnaik'], - action: 'updateLabels', - addLabel: 'a11ymas' - }, + } ] } diff --git a/.github/copycat.yml b/.github/copycat.yml deleted file mode 100644 index eccccc16b007d..0000000000000 --- a/.github/copycat.yml +++ /dev/null @@ -1,5 +0,0 @@ -{ - perform: true, - target_owner: 'chrmarti', - target_repo: 'testissues' -} \ No newline at end of file diff --git a/.github/locker.yml b/.github/locker.yml deleted file mode 100644 index 6d8feccae14c6..0000000000000 --- 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 e5b2cddd8800c..0000000000000 --- 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 7482b60b108ea..0000000000000 --- a/.github/new_release.yml +++ /dev/null @@ -1,6 +0,0 @@ -{ - newReleaseLabel: 'new release', - newReleaseColor: '006b75', - daysAfterRelease: 5, - perform: true -} diff --git a/.github/subscribers.json b/.github/subscribers.json new file mode 100644 index 0000000000000..89dee80d4a9f5 --- /dev/null +++ b/.github/subscribers.json @@ -0,0 +1,7 @@ +{ + "label-to-subscribe-to": [ + "list of usernames to subscribe", + "such as:", + "JacksonKearl" + ] +} diff --git a/.github/workflows/author-verified.yml b/.github/workflows/author-verified.yml new file mode 100644 index 0000000000000..03cd7cedbef3f --- /dev/null +++ b/.github/workflows/author-verified.yml @@ -0,0 +1,39 @@ +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: v35 + 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 + authorVerificationRequestedLabel: author-verification-requested diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55415410f7cd5..d509cce2a33e0 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,92 +127,18 @@ 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 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 --skip-tslint - name: Run Hygiene Checks - - run: yarn gulp tslint - name: Run TSLint Checks - - run: yarn monaco-compile-check - name: Run Monaco Editor 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 --skip-tslint - name: Run Hygiene Checks - - run: yarn gulp tslint - name: Run TSLint Checks - - run: yarn monaco-compile-check - name: Run Monaco Editor 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 --skip-tslint - name: Run Hygiene Checks - - run: yarn gulp tslint - name: Run TSLint Checks - run: yarn monaco-compile-check name: Run Monaco Editor 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 0000000000000..017708844a471 --- /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 0000000000000..f61d114b1579f --- /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: v35 + - 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 0000000000000..9d4c54dc6be5b --- /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: v35 + 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 0000000000000..82ae49039fb56 --- /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: v35 + 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 0000000000000..9fe52cd3477f8 --- /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: v35 + 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/english-please.yml b/.github/workflows/english-please.yml new file mode 100644 index 0000000000000..6cf4d65455cc6 --- /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: v35 + 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 0000000000000..6cdf0bd6fc3f7 --- /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: v35 + - 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 0000000000000..55855054383d1 --- /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: v35 + - 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 0000000000000..2d21b96b8336c --- /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: v35 + - 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 0000000000000..dae985ca35ffb --- /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: v35 + - 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 0000000000000..e51b9ac740fb8 --- /dev/null +++ b/.github/workflows/on-label.yml @@ -0,0 +1,93 @@ +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: v35 + 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 + 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 0000000000000..e985909b446b9 --- /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: v35 + 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 0000000000000..a52b895e51c5a --- /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: v35 + 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 0000000000000..38afb0d7f4929 --- /dev/null +++ b/.github/workflows/rich-navigation.yml @@ -0,0 +1,21 @@ +name: "Rich Navigation Indexing" +on: + pull_request: + push: + branches: + - master + +jobs: + richnav: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + 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 0000000000000..7a173b0e2f321 --- /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: v35 + - 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 160c42ed74bda..0fe46b6eadc4c 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 diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000000..d54a71fdf1f45 --- /dev/null +++ b/.mailmap @@ -0,0 +1,2 @@ +Eric Amodio Eric Amodio +Daniel Imms Daniel Imms diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 91855cc846d98..0000000000000 --- a/.prettierrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "useTabs": true, - "printWidth": 120, - "semi": true, - "singleQuote": true -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b4336e7d127d5..2cd0a32b9ce77 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,7 +2,6 @@ // See https://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ - "ms-vscode.vscode-typescript-tslint-plugin", "dbaeumer.vscode-eslint", "EditorConfig.EditorConfig", "msjsdiag.debugger-for-chrome" diff --git a/.vscode/launch.json b/.vscode/launch.json index 25279b09fd314..d37ac78321bac 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,19 +14,25 @@ { "type": "node", "request": "attach", + "restart": true, "name": "Attach to Extension Host", + "timeout": 30000, "port": 5870, - "restart": true, "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", @@ -35,7 +41,10 @@ "port": 5876, "outFiles": [ "${workspaceFolder}/out/**/*.js" - ] + ], + "presentation": { + "hidden": true, + } }, { "type": "node", @@ -50,10 +59,14 @@ "type": "node", "request": "attach", "name": "Attach to Main Process", + "timeout": 30000, "port": 5875, "outFiles": [ "${workspaceFolder}/out/**/*.js" - ] + ], + "presentation": { + "hidden": true, + } }, { "type": "extensionHost", @@ -67,7 +80,29 @@ ], "outFiles": [ "${workspaceFolder}/out/**/*.js" - ] + ], + "presentation": { + "group": "5_tests", + "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", @@ -82,7 +117,11 @@ ], "outFiles": [ "${workspaceFolder}/out/**/*.js" - ] + ], + "presentation": { + "group": "5_tests", + "order": 3 + } }, { "type": "extensionHost", @@ -96,7 +135,11 @@ ], "outFiles": [ "${workspaceFolder}/out/**/*.js" - ] + ], + "presentation": { + "group": "5_tests", + "order": 4 + } }, { "type": "extensionHost", @@ -110,7 +153,47 @@ ], "outFiles": [ "${workspaceFolder}/out/**/*.js" - ] + ], + "presentation": { + "group": "5_tests", + "order": 5 + } + }, + { + "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": "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": "chrome", @@ -119,7 +202,7 @@ "port": 9222 }, { - "type": "chrome", + "type": "pwa-chrome", "request": "launch", "name": "Launch VS Code", "windows": { @@ -131,22 +214,44 @@ "linux": { "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" }, + "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" + "--no-cached-data", + ], + "webRoot": "${workspaceFolder}", + "cascadeTerminateToConfigurations": [ + "Attach to Extension Host" ], - "webRoot": "${workspaceFolder}" + "userDataDir": false, + "pauseForSourceMap": false, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "browserLaunchLocation": "workspace", + "preLaunchTask": "Ensure Prelaunch Dependencies", + }, + { + "type": "node", + "request": "launch", + "name": "VS Code (Web)", + "program": "${workspaceFolder}/resources/web/code-web.js", + "presentation": { + "group": "0_vscode", + "order": 2 + } }, { "type": "node", "request": "launch", - "name": "Launch VS Code (Main Process)", + "name": "Main Process", "runtimeExecutable": "${workspaceFolder}/scripts/code.sh", "windows": { "runtimeExecutable": "${workspaceFolder}/scripts/code.bat", @@ -156,23 +261,34 @@ ], "outFiles": [ "${workspaceFolder}/out/**/*.js" - ] + ], + "presentation": { + "group": "1_vscode", + "order": 1 + } }, { - "type": "node", + "type": "chrome", "request": "launch", - "name": "Launch VS Code (Web)", - "runtimeExecutable": "yarn", - "runtimeArgs": [ - "web" - ], + "name": "VS Code (Web, Chrome)", + "url": "http://localhost:8080", + "preLaunchTask": "Run web", + "presentation": { + "group": "0_vscode", + "order": 3 + } }, { - "type": "chrome", + "type": "pwa-msedge", "request": "launch", - "name": "Launch VS Code (Web, Chrome)", + "name": "VS Code (Web, Edge)", "url": "http://localhost:8080", - "preLaunchTask": "Run web" + "pauseForSourceMap": false, + "preLaunchTask": "Run web", + "presentation": { + "group": "0_vscode", + "order": 3 + } }, { "type": "node", @@ -183,7 +299,41 @@ "cwd": "${workspaceFolder}/extensions/git", "outFiles": [ "${workspaceFolder}/extensions/git/out/**/*.js" - ] + ], + "presentation": { + "group": "5_tests", + "order": 10 + } + }, + { + "type": "node", + "request": "launch", + "name": "HTML Server Unit Tests", + "program": "${workspaceFolder}/extensions/html-language-features/server/test/index.js", + "stopOnEntry": false, + "cwd": "${workspaceFolder}/extensions/html-language-features/server", + "outFiles": [ + "${workspaceFolder}/extensions/html-language-features/server/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "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", @@ -191,13 +341,17 @@ "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" ], "outFiles": [ "${workspaceFolder}/extensions/markdown-language-features/out/**/*.js" - ] + ], + "presentation": { + "group": "5_tests", + "order": 7 + } }, { "type": "extensionHost", @@ -205,19 +359,23 @@ "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" ], "outFiles": [ "${workspaceFolder}/extensions/typescript-language-features/out/**/*.js" - ] + ], + "presentation": { + "group": "5_tests", + "order": 8 + } }, { "type": "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" @@ -235,6 +393,9 @@ ], "env": { "MOCHA_COLORS": "true" + }, + "presentation": { + "hidden": true } }, { @@ -270,33 +431,52 @@ ], "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": "0_vscode", + "order": 1 + } }, { "name": "Search and Renderer processes", "configurations": [ "Launch VS Code", "Attach to Search Process" - ] + ], + "presentation": { + "group": "1_vscode", + "order": 4 + } }, { "name": "Renderer and Extension Host processes", "configurations": [ "Launch VS Code", "Attach to Extension Host" - ] + ], + "presentation": { + "group": "1_vscode", + "order": 3 + } }, { "name": "Debug Unit Tests", "configurations": [ "Attach to VS Code", "Run Unit Tests" - ] - }, + ], + "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 0000000000000..2eb4b6432b395 --- /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:\"September 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/inbox.github-issues b/.vscode/notebooks/inbox.github-issues new file mode 100644 index 0000000000000..e2e9e8a1cbbe1 --- /dev/null +++ b/.vscode/notebooks/inbox.github-issues @@ -0,0 +1,50 @@ +[ + { + "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": "## 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\" -label:emmet", + "editable": true + }, + { + "kind": 1, + "language": "markdown", + "value": "## Inbox\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 -label:emmet", + "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 0000000000000..a5de65e0bd138 --- /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:\"September 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 0000000000000..4e65452ea56d9 --- /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:\"September 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: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 0000000000000..4b61b3e058545 --- /dev/null +++ b/.vscode/searches/TrustedTypes.code-search @@ -0,0 +1,194 @@ +# Query: .innerHTML = +# Flags: CaseSensitive WordMatch +# Including: src/vs/**/*.{t,j}s +# Excluding: *.test.ts +# ContextLines: 3 + +22 results - 14 files + +src/vs/base/browser/markdownRenderer.ts: + 161 const strValue = values[0]; + 162 const span = element.querySelector(`div[data-code="${id}"]`); + 163 if (span) { + 164: span.innerHTML = strValue; + 165 } + 166 }).catch(err => { + 167 // ignore + + 243 return true; + 244 } + 245 + 246: element.innerHTML = insane(renderedMarkdown, { + 247 allowedSchemes, + 248 // allowedTags should included everything that markdown renders to. + 249 // Since we have our own sanitize function for marked, it's possible we missed some tag so let insane make sure. + +src/vs/base/browser/ui/contextview/contextview.ts: + 157 this.shadowRootHostElement = DOM.$('.shadow-root-host'); + 158 this.container.appendChild(this.shadowRootHostElement); + 159 this.shadowRoot = this.shadowRootHostElement.attachShadow({ mode: 'open' }); + 160: this.shadowRoot.innerHTML = ` + 161 + +src/vs/code/electron-sandbox/issue/issueReporterMain.ts: + 57 const platformClass = platform.isWindows ? 'windows' : platform.isLinux ? 'linux' : 'mac'; + 58 addClass(document.body, platformClass); // used by our fonts + 59 + 60: document.body.innerHTML = BaseHtml(); + 61 const issueReporter = new IssueReporter(configuration); + 62 issueReporter.render(); + 63 document.body.style.display = 'block'; + +src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts: + 320 content.push(`.highest { color: ${styles.highlightForeground}; }`); + 321 } + 322 + 323: styleTag.innerHTML = content.join('\n'); + 324 if (document.head) { + 325 document.head.appendChild(styleTag); + 326 } + +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: + 507 private _finishRenderingNewLines(ctx: IRendererContext, domNodeIsEmpty: boolean, newLinesHTML: string, wasNew: boolean[]): void { + 508 const lastChild = this.domNode.lastChild; + 509 if (domNodeIsEmpty || !lastChild) { + 510: this.domNode.innerHTML = newLinesHTML; + 511 } else { + 512 lastChild.insertAdjacentHTML('afterend', newLinesHTML); + 513 } + + 525 private _finishRenderingInvalidLines(ctx: IRendererContext, invalidLinesHTML: string, wasInvalid: boolean[]): void { + 526 const hugeDomNode = document.createElement('div'); + 527 + 528: hugeDomNode.innerHTML = invalidLinesHTML; + 529 + 530 for (let i = 0; i < ctx.linesLength; i++) { + 531 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/editor/standalone/browser/standaloneThemeServiceImpl.ts: + 212 if (!this._globalStyleElement) { + 213 this._globalStyleElement = dom.createStyleSheet(); + 214 this._globalStyleElement.className = 'monaco-colors'; + 215: this._globalStyleElement.innerHTML = this._css; + 216 this._styleElements.push(this._globalStyleElement); + 217 } + 218 return Disposable.None; + + 221 private _registerShadowDomContainer(domNode: HTMLElement): IDisposable { + 222 const styleElement = dom.createStyleSheet(domNode); + 223 styleElement.className = 'monaco-colors'; + 224: styleElement.innerHTML = this._css; + 225 this._styleElements.push(styleElement); + 226 return { + 227 dispose: () => { + + 291 ruleCollector.addRule(generateTokensCSSForColorMap(colorMap)); + 292 + 293 this._css = cssRules.join('\n'); + 294: this._styleElements.forEach(styleElement => styleElement.innerHTML = this._css); + 295 + 296 TokenizationRegistry.setColorMap(colorMap); + 297 this._onColorThemeChange.fire(theme); + +src/vs/editor/test/browser/controller/imeTester.ts: + 55 let content = this._model.getModelLineContent(i); + 56 r += content + '
'; + 57 } + 58: output.innerHTML = r; + 59 } + 60 } + 61 + + 69 let title = document.createElement('div'); + 70 title.className = 'title'; + 71 + 72: title.innerHTML = description + '. Type ' + inputStr + ''; + 73 container.appendChild(title); + 74 + 75 let startBtn = document.createElement('button'); + +src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts: + 454 + 455 private getMarkdownDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { + 456 const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row'); + 457: dragImageContainer.innerHTML = templateData.container.outerHTML; + 458 + 459 // Remove all rendered content nodes after the + 460 const markdownContent = dragImageContainer.querySelector('.cell.markdown')!; + + 611 return null; + 612 } + 613 + 614: editorContainer.innerHTML = richEditorText; + 615 + 616 return dragImageContainer; + 617 } + +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 { + +src/vs/workbench/contrib/webview/browser/pre/main.js: + 386 // apply default styles + 387 const defaultStyles = newDocument.createElement('style'); + 388 defaultStyles.id = '_defaultStyles'; + 389: defaultStyles.innerHTML = defaultCssRules; + 390 newDocument.head.prepend(defaultStyles); + 391 + 392 applyStyles(newDocument, newDocument.body); + +src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts: + 281 + 282 const content = model.main.textEditorModel.getValue(EndOfLinePreference.LF); + 283 if (!strings.endsWith(input.resource.path, '.md')) { + 284: this.content.innerHTML = content; + 285 this.updateSizeClasses(); + 286 this.decorateContent(); + 287 this.contentDisposables.push(this.keybindingService.onDidUpdateKeybindings(() => this.decorateContent())); + + 303 const innerContent = document.createElement('div'); + 304 innerContent.classList.add('walkThroughContent'); // only for markdown files + 305 const markdown = this.expandMacros(content); + 306: innerContent.innerHTML = marked(markdown, { renderer }); + 307 this.content.appendChild(innerContent); + 308 + 309 model.snippets.forEach((snippet, i) => { diff --git a/.vscode/searches/es6.code-search b/.vscode/searches/es6.code-search new file mode 100644 index 0000000000000..1e1bdd94188e3 --- /dev/null +++ b/.vscode/searches/es6.code-search @@ -0,0 +1,61 @@ +# Query: @deprecated ES6 +# Flags: CaseSensitive WordMatch +# ContextLines: 2 + +12 results - 4 files + +src/vs/base/browser/dom.ts: + 83 }; + 84 + 85: /** @deprecated ES6 - use classList*/ + 86 export const hasClass: (node: HTMLElement | SVGElement, className: string) => boolean = _classList.hasClass.bind(_classList); + 87: /** @deprecated ES6 - use classList*/ + 88 export const addClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.addClass.bind(_classList); + 89: /** @deprecated ES6 - use classList*/ + 90 export const addClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.addClasses.bind(_classList); + 91: /** @deprecated ES6 - use classList*/ + 92 export const removeClass: (node: HTMLElement | SVGElement, className: string) => void = _classList.removeClass.bind(_classList); + 93: /** @deprecated ES6 - use classList*/ + 94 export const removeClasses: (node: HTMLElement | SVGElement, ...classNames: string[]) => void = _classList.removeClasses.bind(_classList); + 95: /** @deprecated ES6 - use classList*/ + 96 export const toggleClass: (node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean) => void = _classList.toggleClass.bind(_classList); + 97 + +src/vs/base/common/arrays.ts: + 401 + 402 /** + 403: * @deprecated ES6: use `Array.find` + 404 */ + 405 export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; + +src/vs/base/common/objects.ts: + 115 + 116 /** + 117: * @deprecated ES6 + 118 */ + 119 export function assign(destination: T): T; + +src/vs/base/common/strings.ts: + 15 + 16 /** + 17: * @deprecated ES6: use `String.padStart` + 18 */ + 19 export function pad(n: number, l: number, char: string = '0'): string { + + 146 + 147 /** + 148: * @deprecated ES6: use `String.startsWith` + 149 */ + 150 export function startsWith(haystack: string, needle: string): boolean { + + 167 + 168 /** + 169: * @deprecated ES6: use `String.endsWith` + 170 */ + 171 export function endsWith(haystack: string, needle: string): boolean { + + 857 + 858 /** + 859: * @deprecated ES6 + 860 */ + 861 export function repeat(s: string, count: number): string { diff --git a/.vscode/searches/ts36031.code-search b/.vscode/searches/ts36031.code-search new file mode 100644 index 0000000000000..51071b8840af7 --- /dev/null +++ b/.vscode/searches/ts36031.code-search @@ -0,0 +1,53 @@ +# Query: \\w+\\?\\.\\w+![(.[] +# Flags: RegExp +# ContextLines: 2 + +8 results - 4 files + +src/vs/base/browser/ui/tree/asyncDataTree.ts: + 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 ef1f7370d26e3..9239eae0d4dbf 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", @@ -37,6 +42,11 @@ } } ], + "eslint.options": { + "rulePaths": [ + "./build/lib/eslint" + ] + }, "typescript.tsdk": "node_modules/typescript/lib", "npm.exclude": "**/extensions/**", "npm.packageManager": "yarn", @@ -62,5 +72,9 @@ "msjsdiag.debugger-for-chrome": "workspace" }, "gulp.autoDetect": "off", - "files.insertFinalNewline": true + "files.insertFinalNewline": true, + "[typescript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "typescript.tsc.autoDetect": "off" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d1a697bb41daa..943ae5bd53644 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -3,12 +3,34 @@ "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" }, + "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": "watch-extensionsd", + "label": "Build VS Code Extensions", "isBackground": true, "presentation": { "reveal": "never" @@ -31,27 +53,81 @@ } } }, + { + "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": "strict-initialization-watch", - "label": "TS - Strict Initialization", + "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": { - "base": "$tsc-watch", - "owner": "typescript-strict-initialization", - "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" + } } }, { - "type": "gulp", - "task": "tslint", - "label": "Run tslint", - "problemMatcher": [ - "$tslint5" - ] + "type": "npm", + "script": "kill-watch-webd", + "label": "Kill Build Web Extensions", + "group": "build", + "presentation": { + "reveal": "never" + }, + "problemMatcher": "$tsc" }, { "label": "Run tests", @@ -87,10 +163,9 @@ }, { "type": "shell", - "command": "yarn web -- --no-launch", + "command": "yarn web --no-launch", "label": "Run web", "isBackground": true, - // This section to make error go away when launching the debug config "problemMatcher": { "pattern": { "regexp": "" @@ -104,5 +179,35 @@ "reveal": "never" } }, + { + "type": "npm", + "script": "eslint", + "problemMatcher": { + "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 4c8b70bf6f6ae..fbf612d0989e4 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "6.1.2" +target "9.3.0" runtime "electron" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 34703fae665bd..b3eb18c2bc0d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 @@ -87,10 +87,11 @@ Once submitted, your report will go into the [issue tracking](https://github.com ## 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. diff --git a/LICENSE.txt b/LICENSE.txt index 69be21bd8ed08..0ac28ee234d23 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 5d39d7e40c616..c095a1d190b69 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # 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) +[![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) @@ -22,8 +20,6 @@ This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Stu Visual Studio Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [Visual Studio Code's website](https://code.visualstudio.com/Download). To get the latest releases every day, install the [Insiders build](https://code.visualstudio.com/insiders). - - ## Contributing There are many ways in which you can participate in the project, for example: @@ -46,17 +42,26 @@ 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) +* 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! ## Related Projects -Many of the core components and extensions to 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 -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`. +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 diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 440edf6844ba8..4cf2d042f8023 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) -4. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass) +3. atom/language-java version 0.31.4 (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.9 (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.2 (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) +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/BackgroundSync) %% 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 @@ -589,7 +614,7 @@ END OF HTML 5.1 W3C Working Draft NOTICES AND INFORMATION ========================================= MIT License -Copyright (c) 2017 Yuki Ueda +Copyright (c) 2019 Yuki Ueda Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -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) @@ -1692,10 +1640,10 @@ 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 @@ -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/azure-pipelines.yml b/azure-pipelines.yml index e52afca602874..b2f9a13c2b7b9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -13,6 +13,6 @@ jobs: - job: macOS pool: - vmImage: macOS 10.13 + vmImage: macOS-latest steps: - - template: build/azure-pipelines/darwin/continuous-build-darwin.yml \ No newline at end of file + - template: build/azure-pipelines/darwin/continuous-build-darwin.yml diff --git a/build/.cachesalt b/build/.cachesalt index 339d2d379fa4b..3f2ee542ad5fc 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/.nativeignore b/build/.nativeignore index a4823bf719cef..0e0eb08b283ad 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/** @@ -83,13 +76,13 @@ node-pty/deps/** !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 0000000000000..1035cb291cc19 --- /dev/null +++ b/build/.webignore @@ -0,0 +1,27 @@ +# cleanup rules for web node modules, .gitignore style + +**/*.txt +**/*.json +**/*.md +**/*.d.ts +**/*.js.map +**/LICENSE +**/CONTRIBUTORS + +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 619c92a04120e..d7e62629cb847 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 84bbd9c537c17..6436e93c8c1e1 100755 --- a/build/azure-pipelines/common/extract-telemetry.sh +++ b/build/azure-pipelines/common/extract-telemetry.sh @@ -10,10 +10,10 @@ 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 . +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 143b61bb61abb..b1947ebc024f6 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/releaseBuild.ts b/build/azure-pipelines/common/releaseBuild.ts index 13d40a960e5f3..ac49e7b0042a8 100644 --- a/build/azure-pipelines/common/releaseBuild.ts +++ b/build/azure-pipelines/common/releaseBuild.ts @@ -17,13 +17,46 @@ function getEnv(name: string): string { return result; } +interface Config { + id: string; + frozen: boolean; +} + +function createDefaultConfig(quality: string): Config { + return { + id: quality, + frozen: false + }; +} + +async function getConfig(client: CosmosClient, quality: string): Promise { + const query = `SELECT TOP 1 * FROM c WHERE c.id = "${quality}"`; + + const res = await client.database('builds').container('config').items.query(query).fetchAll(); + + if (res.resources.length === 0) { + return createDefaultConfig(quality); + } + + return res.resources[0] as Config; +} + async function main(): Promise { const commit = getEnv('BUILD_SOURCEVERSION'); const quality = getEnv('VSCODE_QUALITY'); + const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, key: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const config = await getConfig(client, quality); + + console.log('Quality config:', config); + + if (config.frozen) { + console.log(`Skipping release because quality ${quality} is frozen.`); + return; + } + console.log(`Releasing build ${commit}...`); - const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, key: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); const scripts = client.database('builds').container(quality).scripts; await scripts.storedProcedure('releaseBuild').execute('', [commit]); } diff --git a/build/azure-pipelines/common/symbols.ts b/build/azure-pipelines/common/symbols.ts deleted file mode 100644 index 153be4f25b1c2..0000000000000 --- 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 0000000000000..90031d937be48 --- /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 476ee9137a1bd..631b9af7f1068 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -1,49 +1,70 @@ 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: '$(ArtifactFeed)' -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + 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: '$(ArtifactFeed)' + vstsFeed: 'vscode-build-cache' condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | yarn electron x64 displayName: Download Electron -- script: | - yarn gulp hygiene --skip-tslint - displayName: Run Hygiene Checks -- script: | - yarn gulp tslint - displayName: Run TSLint 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 0000000000000..4efe1ce508f85 --- /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 0000000000000..7cd9df032bd08 --- /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 0000000000000..be8b7163da7fc --- /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 273a728927f25..3b186bb11360c 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 + 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 - pushd ../VSCode-darwin && zip -r -X -y ../VSCode-darwin.zip * && popd + 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,76 @@ 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)" \ ./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 a8067a5eefb96..07734e194f86e 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 \ @@ -20,9 +17,3 @@ node build/azure-pipelines/common/createAsset.js \ 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 diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index 4689451b54e74..f9bdf7fef8e81 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 b91b01138d01b..cf1ced09dc8a5 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 a87e3753e77a3..41225110f3737 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -7,51 +7,77 @@ 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: '$(ArtifactFeed)' -- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 - inputs: - versionSpec: "1.x" + 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: '$(ArtifactFeed)' + vstsFeed: 'vscode-build-cache' condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - script: | yarn electron x64 displayName: Download Electron + - script: | - yarn gulp hygiene --skip-tslint + yarn gulp hygiene displayName: Run Hygiene Checks -- script: | - yarn gulp tslint - displayName: Run TSLint 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 68ae4ee8b67f9..258f87ea3d2dc 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 6e1f8ec1e536b..270beb898a0fc 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,53 +89,128 @@ 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 + 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: succeededOrFailed() + +- 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' + Pattern: '*.rpm' + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "keyCode": "CP-450779-Pgp", + "operationSetCode": "LinuxSign", + "parameters": [ ], + "toolName": "sign", + "toolVersion": "1.0" + } + ] + SessionTimeout: 120 + displayName: Codesign rpm - script: | set -e 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 3a5f9683eaac9..72fe2ad7b30e4 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,32 +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 -yarn gulp "vscode-linux-x64-build-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 -yarn gulp "vscode-linux-x64-build-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 -yarn gulp "vscode-linux-x64-prepare-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 a530499b31305..39c39e86c9e1d 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 4d77d253a2649..2365c09f3a460 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 index efb7d4d1ca94b..e133b2d2bb9f4 100644 --- a/build/azure-pipelines/mixin.js +++ b/build/azure-pipelines/mixin.js @@ -12,6 +12,8 @@ const es = require('event-stream'); const vfs = require('vinyl-fs'); const fancyLog = require('fancy-log'); const ansiColors = require('ansi-colors'); +const fs = require('fs'); +const path = require('path'); function main() { const quality = process.env['VSCODE_QUALITY']; @@ -21,7 +23,7 @@ function main() { return; } - const productJsonFilter = filter('product.json', { restore: true }); + const productJsonFilter = filter(f => f.relative === 'product.json', { restore: true }); fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`); return vfs @@ -29,7 +31,32 @@ function main() { .pipe(filter(f => !f.isDirectory())) .pipe(productJsonFilter) .pipe(buffer()) - .pipe(json(o => Object.assign({}, require('../product.json'), o))) + .pipe(json(o => { + const ossProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8')); + 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) { fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); @@ -38,4 +65,4 @@ function main() { .pipe(vfs.dest('.')); } -main(); \ No newline at end of file +main(); diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index ecf47fa1cdd59..7d4246f4b3f3d 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -1,150 +1,159 @@ +trigger: none +pr: none + +schedules: +- cron: "0 5 * * Mon-Fri" + displayName: Mon-Fri at 7:00 + 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 -jobs: -- job: Compile - pool: - vmImage: 'Ubuntu-16.04' - container: vscode-x64 - steps: - - template: product-compile.yml +stages: +- stage: Compile + 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 +- stage: Windows 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')) + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) pool: vmImage: VS2017-Win2016 - variables: - VSCODE_ARCH: ia32 - dependsOn: - - Compile - steps: - - template: win32/product-build-win32.yml + jobs: + - job: Windows + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true')) + variables: + VSCODE_ARCH: x64 + 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: Windows32 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true')) + variables: + VSCODE_ARCH: ia32 + steps: + - template: win32/product-build-win32.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: WindowsARM64 + condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32_ARM64'], 'true')) + variables: + VSCODE_ARCH: arm64 + steps: + - template: win32/product-build-win32-arm64.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 +- stage: Linux 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'), ne(variables['VSCODE_QUALITY'], 'stable')) + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) pool: vmImage: 'Ubuntu-16.04' - variables: - VSCODE_ARCH: arm64 - dependsOn: - - Compile - steps: - - template: linux/product-build-linux-multiarch.yml + 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: 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: 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: 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: 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: macOS - condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_MACOS'], 'true')) - pool: - vmImage: macOS 10.13 + - 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 - 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')))) + condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) pool: - vmImage: 'Ubuntu-16.04' + vmImage: macOS-latest + jobs: + - job: macOS + condition: and(succeeded(), eq(variables['VSCODE_BUILD_MACOS'], 'true')) + steps: + - template: darwin/product-build-darwin.yml + +- stage: Mooncake dependsOn: - Windows - - Windows32 - Linux - - LinuxSnap - - LinuxArmhf - - LinuxAlpine - macOS - steps: - - template: release.yml - -- job: Mooncake + condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) pool: vmImage: 'Ubuntu-16.04' - condition: and(succeededOrFailed(), eq(variables['VSCODE_COMPILE_ONLY'], 'false')) + jobs: + - job: SyncMooncake + displayName: Sync Mooncake + steps: + - template: sync-mooncake.yml + +- stage: Publish dependsOn: - Windows - - Windows32 - Linux - - LinuxSnap - - LinuxArmhf - - LinuxAlpine - - LinuxWeb - macOS - steps: - - template: sync-mooncake.yml - -trigger: none -pr: none - -schedules: -- cron: "0 5 * * Mon-Fri" - displayName: Mon-Fri at 7:00 - branches: - include: - - master + 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 87c70f4f8053f..2eff40b53d106 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -12,23 +12,24 @@ steps: vstsFeed: 'npm-vscode' platformIndependent: true alias: 'Compilation' + dryRun: true - task: NodeTool@0 inputs: - versionSpec: "12.13.0" - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + versionSpec: "12.14.1" + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 inputs: versionSpec: "1.x" - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: AzureKeyVault@1 displayName: 'Azure Key Vault: Get Secrets' inputs: azureSubscription: 'vscode-builds-subscription' KeyVaultName: vscode - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -41,7 +42,7 @@ steps: git config user.email "vscode@microsoft.com" git config user.name "VSCode" displayName: Prepare tooling - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -49,33 +50,37 @@ steps: git fetch distro git merge $(node -p "require('./package.json').distro") displayName: Merge distro - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + 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['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e CHILD_CONCURRENCY=1 yarn --frozen-lockfile displayName: Install dependencies - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), 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-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), ne(variables['CacheRestored'], 'true')) - script: | set -e yarn postinstall displayName: Run postinstall scripts - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['CacheRestored'], 'true')) # Mixin must run before optimize, because the CSS loader will # inline small SVGs @@ -83,28 +88,28 @@ steps: set -e node build/azure-pipelines/mixin displayName: Mix in quality - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e - yarn gulp hygiene --skip-tslint - yarn gulp tslint + yarn gulp hygiene yarn monaco-compile-check - displayName: Run hygiene, tslint and monaco compile checks - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + yarn valid-layers-check + displayName: Run hygiene, monaco compile & valid layers checks + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set - ./build/azure-pipelines/common/extract-telemetry.sh displayName: Extract Telemetry - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e AZURE_WEBVIEW_STORAGE_ACCESS_KEY="$(vscode-webview-storage-key)" \ ./build/azure-pipelines/common/publish-webview.sh displayName: Publish Webview - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -114,14 +119,14 @@ steps: yarn gulp minify-vscode-reh yarn gulp minify-vscode-reh-web displayName: Compile - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ node build/azure-pipelines/upload-sourcemaps displayName: Upload sourcemaps - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - script: | set -e @@ -129,7 +134,7 @@ steps: AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ node build/azure-pipelines/common/createBuild.js $VERSION displayName: Create build - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: @@ -138,4 +143,4 @@ steps: vstsFeed: 'npm-vscode' platformIndependent: true alias: 'Compilation' - condition: and(succeeded(), ne(variables['CacheRestored-Compilation'], 'true')) + condition: and(succeeded(), ne(variables['CacheExists-Compilation'], 'true')) diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml index 1d4ab83e1ac0a..10b6aa4e16af5 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 a5ef449b77b8c..9603726bebfcc 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) { diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml index 2641830a4130f..49dfc9ced80d7 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/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 0c338203b4d6b..7f4907aa2d9f4 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: diff --git a/build/azure-pipelines/win32/ESRPClient/packages.config b/build/azure-pipelines/win32/ESRPClient/packages.config index d7a6f144f4778..c10bed141215a 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 7e16a00f0d898..8600377139c2d 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -1,54 +1,77 @@ 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: '$(ArtifactFeed)' + 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: '$(ArtifactFeed)' + vstsFeed: 'vscode-build-cache' condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) + - powershell: | yarn electron -- script: | - yarn gulp hygiene --skip-tslint - displayName: Run Hygiene Checks -- script: | - yarn gulp tslint - displayName: Run TSLint 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/product-build-win32-arm64.yml b/build/azure-pipelines/win32/product-build-win32-arm64.yml new file mode 100644 index 0000000000000..ecb50ad678ed2 --- /dev/null +++ b/build/azure-pipelines/win32/product-build-win32-arm64.yml @@ -0,0 +1,190 @@ +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' + +- powershell: | + $ErrorActionPreference = "Stop" + .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(esrp-auth-certificate) -AuthCertificateKey $(esrp-auth-certificate-key) + 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 4c9336c6c1c5a..be80731a7ab2f 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 @@ -204,7 +243,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 5a22d4749cf6c..a225f9d5fdf9c 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 00c4d42d9dbff..840cbe4071f8a 100644 --- a/build/azure-pipelines/win32/sign.ps1 +++ b/build/azure-pipelines/win32/sign.ps1 @@ -67,4 +67,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.1.2.25\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 15fc5ec1d2952..0000000000000 --- a/build/builtInExtensions.json +++ /dev/null @@ -1,47 +0,0 @@ -[ - { - "name": "ms-vscode.node-debug", - "version": "1.40.1", - "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.39.3", - "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.31", - "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 60b30655c0cb3..e5956179567c9 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 b094a67cac5c7..7379de7a93d2e 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.js b/build/darwin/sign.js new file mode 100644 index 0000000000000..b8eb9fc752503 --- /dev/null +++ b/build/darwin/sign.js @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- + * 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 codesign = require("electron-osx-sign"); +const path = require("path"); +const util = require("../lib/util"); +const product = require("../../product.json"); +async function main() { + 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 = { + 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 = Object.assign(Object.assign({}, defaultOpts), { + // TODO(deepak1556): Incorrectly declared type in electron-osx-sign + ignore: (filePath) => { + return filePath.includes(gpuHelperAppName) || + filePath.includes(pluginHelperAppName) || + filePath.includes(rendererHelperAppName); + } }); + const gpuHelperOpts = Object.assign(Object.assign({}, 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 = Object.assign(Object.assign({}, 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 = Object.assign(Object.assign({}, 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); +} +if (require.main === module) { + main().catch(err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts new file mode 100644 index 0000000000000..ee5d2eeb17b84 --- /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 6734a820cf181..6cb65c5a16681 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -16,15 +16,17 @@ const cp = require('child_process'); const compilation = require('./lib/compilation'); const monacoapi = require('./monaco/api'); const fs = require('fs'); +const webpack = require('webpack'); +const webpackGulp = require('webpack-stream'); -var root = path.dirname(__dirname); -var sha1 = util.getVersion(root); -var semver = require('./monaco/package.json').version; -var headerVersion = semver + '(' + sha1 + ')'; +let root = path.dirname(__dirname); +let sha1 = util.getVersion(root); +let semver = require('./monaco/package.json').version; +let headerVersion = semver + '(' + sha1 + ')'; // Build -var editorEntryPoints = [ +let editorEntryPoints = [ { name: 'vs/editor/editor.main', include: [], @@ -40,11 +42,11 @@ var editorEntryPoints = [ } ]; -var editorResources = [ - 'out-editor-build/vs/base/browser/ui/codiconLabel/**/*.ttf' +let editorResources = [ + 'out-editor-build/vs/base/browser/ui/codicons/**/*.ttf' ]; -var BUNDLED_FILE_HEADER = [ +let BUNDLED_FILE_HEADER = [ '/*!-----------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', ' * Version: ' + headerVersion, @@ -57,7 +59,6 @@ var BUNDLED_FILE_HEADER = [ const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); const extractEditorSrcTask = task.define('extract-editor-src', () => { - console.log(`If the build fails, consider tweaking shakeLevel below to a lower value.`); const apiusages = monacoapi.execute().usageContent; const extrausages = fs.readFileSync(path.join(root, 'build', 'monaco', 'monaco.usage.recipe')).toString(); standalone.extractEditor({ @@ -71,21 +72,8 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { apiusages, extrausages ], - typings: [ - 'typings/lib.ie11_safe_es6.d.ts', - 'typings/thenable.d.ts', - 'typings/es6-promise.d.ts', - 'typings/require-monaco.d.ts', - "typings/lib.es2018.promise.d.ts", - 'vs/monaco.d.ts' - ], - 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: [] }); @@ -138,23 +126,81 @@ 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') { - const result = cp.spawnSync(`..\\node_modules\\.bin\\tsc.cmd`, { + result = cp.spawnSync(`..\\node_modules\\.bin\\tsc.cmd`, { cwd: path.join(__dirname, '../out-editor-esm') }); - console.log(result.stdout.toString()); - console.log(result.stderr.toString()); } else { - const result = cp.spawnSync(`node`, [`../node_modules/.bin/tsc`], { + result = cp.spawnSync(`node`, [`../node_modules/.bin/tsc`], { cwd: path.join(__dirname, '../out-editor-esm') }); - console.log(result.stdout.toString()); - console.log(result.stderr.toString()); + } + + console.log(result.stdout.toString()); + console.log(result.stderr.toString()); + + 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'); + 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')); + + 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 + }); + } + + // copy files from tree shaken src + for (const file of files) { + const srcFilePath = path.join(__dirname, '../out-editor-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); + } + } + + console.log(`Open in VS Code the folder at '${destPath}' and you can alayze the compilation error`); + throw new Error('Standalone Editor compilation failed. If this is the build machine, simply launch `yarn run gulp editor-distro` on your machine to further analyze the compilation problem.'); + }); } }); function toExternalDTS(contents) { - let lines = contents.split('\n'); + let lines = contents.split(/\r\n|\r|\n/); let killNextCloseCurlyBrace = false; for (let i = 0; i < lines.length; i++) { let line = lines[i]; @@ -184,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) { @@ -220,7 +271,7 @@ const finalEditorResourcesTask = task.define('final-editor-resources', () => { // package.json gulp.src('build/monaco/package.json') .pipe(es.through(function (data) { - var json = JSON.parse(data.contents.toString()); + let json = JSON.parse(data.contents.toString()); json.private = false; data.contents = Buffer.from(JSON.stringify(json, null, ' ')); this.emit('data', data); @@ -264,10 +315,10 @@ const finalEditorResourcesTask = task.define('final-editor-resources', () => { return; } - var relativePathToMap = path.relative(path.join(data.relative), path.join('min-maps', data.relative + '.map')); + let relativePathToMap = path.relative(path.join(data.relative), path.join('min-maps', data.relative + '.map')); - var strContents = data.contents.toString(); - var newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/'); + let strContents = data.contents.toString(); + let newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/'); strContents = strContents.replace(/\/\/# sourceMappingURL=[^ ]+$/, newStr); data.contents = Buffer.from(strContents); @@ -284,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( @@ -310,6 +368,56 @@ 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); + fs.writeFileSync(path.join(root, 'src/vs/editor/common/standalone/standaloneEnums.ts'), result.enums); + return Promise.resolve(true); +})); + //#region monaco type checking function createTscCompileTask(watch) { @@ -348,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 ca28c413a71ec..6ae72e4cf0631 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'); @@ -109,7 +113,8 @@ const tasks = compilations.map(function (tsconfigFile) { const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false, true); - const input = pipeline.tsProjectSrc(); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); return input .pipe(pipeline()) @@ -118,7 +123,8 @@ const tasks = compilations.map(function (tsconfigFile) { const watchTask = task.define(`watch-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false); - const input = pipeline.tsProjectSrc(); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); const watchInput = watcher(src, { ...srcOpts, ...{ readDelay: 200 } }); return watchInput @@ -128,7 +134,8 @@ const tasks = compilations.map(function (tsconfigFile) { const compileBuildTask = task.define(`compile-build-extension-${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(true, true); - const input = pipeline.tsProjectSrc(); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); return input .pipe(pipeline()) @@ -158,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 a8bb8898c90fb..4952ee1c26d58 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -8,10 +8,8 @@ const gulp = require('gulp'); const filter = require('gulp-filter'); const es = require('event-stream'); -const gulptslint = require('gulp-tslint'); const gulpeslint = require('gulp-eslint'); const tsfmt = require('typescript-formatter'); -const tslint = require('tslint'); const VinylFile = require('vinyl'); const vfs = require('vinyl-fs'); const path = require('path'); @@ -35,6 +33,7 @@ const all = [ 'scripts/**/*', 'src/**/*', 'test/**/*', + '!test/**/out/**', '!**/node_modules/**' ]; @@ -42,8 +41,8 @@ const indentationFilter = [ '**', // except specific files - '!ThirdPartyNotices.txt', - '!LICENSE.{txt,rtf}', + '!**/ThirdPartyNotices.txt', + '!**/LICENSE.{txt,rtf}', '!LICENSES.chromium.html', '!**/LICENSE', '!src/vs/nls.js', @@ -55,11 +54,12 @@ const indentationFilter = [ '!src/vs/base/common/marked/marked.js', '!src/vs/base/node/terminateProcess.sh', '!src/vs/base/node/cpuUsage.sh', - '!test/assert.js', + '!test/unit/assert.js', // 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/**', @@ -84,8 +84,8 @@ const indentationFilter = [ '!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,tslintRules,download}/**/*.js', + '!**/*.{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', @@ -115,20 +115,19 @@ const copyrightFilter = [ '!**/*.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/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', - '!scripts/code-web.js' + '!src/vs/editor/test/node/classification/typescript-test.ts' ]; -const eslintFilter = [ +const jsHygieneFilter = [ 'src/**/*.js', 'build/gulpfile.*.js', '!src/vs/loader.js', @@ -141,7 +140,10 @@ const eslintFilter = [ '!**/test/**' ]; -const tslintBaseFilter = [ +const tsHygieneFilter = [ + 'src/**/*.ts', + 'test/**/*.ts', + 'extensions/**/*.ts', '!**/fixtures/**', '!**/typings/**', '!**/node_modules/**', @@ -152,30 +154,6 @@ const tslintBaseFilter = [ '!extensions/html-language-features/server/lib/jquery.d.ts' ]; -const tslintCoreFilter = [ - 'src/**/*.ts', - 'test/**/*.ts', - '!extensions/**/*.ts', - '!test/automation/**', - '!test/smoke/**', - ...tslintBaseFilter -]; - -const tslintExtensionsFilter = [ - 'extensions/**/*.ts', - '!src/**/*.ts', - '!test/**/*.ts', - 'test/automation/**/*.ts', - ...tslintBaseFilter -]; - -const tslintHygieneFilter = [ - 'src/**/*.ts', - 'test/**/*.ts', - 'extensions/**/*.ts', - ...tslintBaseFilter -]; - const copyrightHeaderLines = [ '/*---------------------------------------------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', @@ -185,27 +163,17 @@ const copyrightHeaderLines = [ gulp.task('eslint', () => { return vfs.src(all, { base: '.', follow: true, allowEmpty: true }) - .pipe(filter(eslintFilter)) - .pipe(gulpeslint('src/.eslintrc')) + .pipe(filter(jsHygieneFilter.concat(tsHygieneFilter))) + .pipe(gulpeslint({ + configFile: '.eslintrc.json', + rulePaths: ['./build/lib/eslint'] + })) .pipe(gulpeslint.formatEach('compact')) - .pipe(gulpeslint.failAfterError()); -}); - -gulp.task('tslint', () => { - return es.merge([ - - // Core: include type information (required by certain rules like no-nodejs-globals) - vfs.src(all, { base: '.', follow: true, allowEmpty: true }) - .pipe(filter(tslintCoreFilter)) - .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint', program: tslint.Linter.createProgram('src/tsconfig.json') })) - .pipe(gulptslint.default.report({ emitError: true })), - - // Exenstions: do not include type information - vfs.src(all, { base: '.', follow: true, allowEmpty: true }) - .pipe(filter(tslintExtensionsFilter)) - .pipe(gulptslint.default({ rulesDirectory: 'build/lib/tslint' })) - .pipe(gulptslint.default.report({ emitError: true })) - ]).pipe(es.through()); + .pipe(gulpeslint.results(results => { + if (results.warningCount > 0 || results.errorCount > 0) { + throw new Error('eslint failed with warnings and/or errors'); + } + })); }); function checkPackageJSON(actualPath) { @@ -227,7 +195,7 @@ function checkPackageJSON(actualPath) { const checkPackageJSONTask = task.define('check-package-json', () => { return gulp.src('package.json') - .pipe(es.through(function() { + .pipe(es.through(function () { checkPackageJSON.call(this, 'remote/package.json'); checkPackageJSON.call(this, 'remote/web/package.json'); })); @@ -294,8 +262,6 @@ function hygiene(some) { replace: undefined, tsconfig: undefined, tsconfigFile: undefined, - tslint: undefined, - tslintFile: undefined, tsfmtFile: undefined, vscode: undefined, vscodeFile: undefined @@ -304,7 +270,7 @@ function hygiene(some) { 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); + console.error('File not formatted. Run the \'Format Document\' command to fix it:', file.relative); errorCount++; } cb(null, file); @@ -314,20 +280,15 @@ function hygiene(some) { }); }); - const tslintConfiguration = tslint.Configuration.findConfiguration('tslint.json', '.'); - const tslintOptions = { fix: false, formatter: 'json' }; - const tsLinter = new tslint.Linter(tslintOptions); - - const tsl = es.through(function (file) { - const contents = file.contents.toString('utf8'); - tsLinter.lint(file.relative, contents, tslintConfiguration.results); - this.emit('data', file); - }); - let input; if (Array.isArray(some) || typeof some === 'string' || !some) { - input = vfs.src(some || all, { base: '.', follow: true, allowEmpty: true }); + 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; } @@ -344,19 +305,21 @@ function hygiene(some) { .pipe(filter(copyrightFilter)) .pipe(copyrights); - let typescript = result - .pipe(filter(tslintHygieneFilter)) + const typescript = result + .pipe(filter(tsHygieneFilter)) .pipe(formatting); - if (!process.argv.some(arg => arg === '--skip-tslint')) { - typescript = typescript.pipe(tsl); - } - const javascript = result - .pipe(filter(eslintFilter)) - .pipe(gulpeslint('src/.eslintrc')) + .pipe(filter(jsHygieneFilter.concat(tsHygieneFilter))) + .pipe(gulpeslint({ + configFile: '.eslintrc.json', + rulePaths: ['./build/lib/eslint'] + })) .pipe(gulpeslint.formatEach('compact')) - .pipe(gulpeslint.failAfterError()); + .pipe(gulpeslint.results(results => { + errorCount += results.warningCount; + errorCount += results.errorCount; + })); let count = 0; return es.merge(typescript, javascript) @@ -368,20 +331,6 @@ function hygiene(some) { this.emit('data', data); }, function () { process.stdout.write('\n'); - - const tslintResult = tsLinter.getResult(); - if (tslintResult.failures.length > 0) { - for (const failure of tslintResult.failures) { - const name = failure.getFileName(); - const position = failure.getStartPosition(); - const line = position.getLineAndCharacter().line; - const character = position.getLineAndCharacter().character; - - console.error(`${name}:${line + 1}:${character + 1}:${failure.getFailure()}`); - } - errorCount += tslintResult.failures.length; - } - if (errorCount > 0) { this.emit('error', 'Hygiene failed with ' + errorCount + ' errors. Check \'build/gulpfile.hygiene.js\'.'); } else { diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index a956fba979ef6..5f367d1f0777d 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'); @@ -118,7 +110,7 @@ function mixinServer(watch) { const packageJSONPath = path.join(path.dirname(__dirname), 'package.json'); function exec(cmdLine) { console.log(cmdLine); - cp.execSync(cmdLine, { stdio: "inherit" }); + cp.execSync(cmdLine, { stdio: 'inherit' }); } function checkout() { const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString()); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 93a943aba0be0..d046aa71ed2f2 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,27 +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/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/**' ]; @@ -90,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 }) )); @@ -102,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); @@ -120,9 +113,9 @@ gulp.task(minifyVSCodeTask); * @return {Object} A map of paths to checksums. */ function computeChecksums(out, filenames) { - var result = {}; + let result = {}; filenames.forEach(function (filename) { - var fullPath = path.join(process.cwd(), out, filename); + let fullPath = path.join(process.cwd(), out, filename); result[filename] = computeChecksum(fullPath); }); return result; @@ -135,9 +128,9 @@ function computeChecksums(out, filenames) { * @return {string} The checksum for `filename`. */ function computeChecksum(filename) { - var contents = fs.readFileSync(filename); + let contents = fs.readFileSync(filename); - var hash = crypto + let hash = crypto .createHash('md5') .update(contents) .digest('base64') @@ -156,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' ]); @@ -212,7 +207,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const deps = gulp.src(dependenciesSrc, { base: '.', dot: true }) .pipe(filter(['**', '!**/package-lock.json'])) .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(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*', '**/*.wasm'], 'app/node_modules.asar')); let all = es.merge( packageJsonStream, @@ -268,7 +263,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') { @@ -325,10 +320,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 => { @@ -427,7 +423,7 @@ gulp.task('vscode-translations-pull', function () { }); gulp.task('vscode-translations-import', function () { - var options = minimist(process.argv.slice(2), { + let options = minimist(process.argv.slice(2), { string: 'location', default: { location: '../vscode-translations-import' @@ -457,20 +453,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 9d3a7caa07e68..1d8a09e4fe617 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; - - { - 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))); - // 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); - } +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 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 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 497fc553c038a..2abc39976b42f 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -65,6 +65,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 +89,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 +113,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 +130,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 +148,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/lib/asar.js b/build/lib/asar.js index 21c5f65a45b06..f48583c0b46c6 100644 --- a/build/lib/asar.js +++ b/build/lib/asar.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.createAsar = void 0; const path = require("path"); const es = require("event-stream"); const pickle = require('chromium-pickle-js'); @@ -52,7 +53,9 @@ function createAsar(folderPath, unpackGlobs, destFilename) { const insertFile = (relativePath, stat, shouldUnpack) => { 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) { if (file.stat.isDirectory()) { diff --git a/build/lib/asar.ts b/build/lib/asar.ts index d2823043aabfc..38fbc634e990d 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) { diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index a687bbccb4cb5..f86414211ec0c 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -18,8 +18,17 @@ const fancyLog = require('fancy-log'); const ansiColors = require('ansi-colors'); const root = path.dirname(path.dirname(__dirname)); -const builtInExtensions = require('../builtInExtensions.json'); +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() { + if (ENABLE_LOGGING) { + fancyLog.apply(this, arguments); + } +} function getExtensionPath(extension) { return path.join(root, '.build', 'builtInExtensions', extension.name); @@ -44,7 +53,7 @@ function isUpToDate(extension) { function syncMarketplaceExtension(extension) { if (isUpToDate(extension)) { - fancyLog(ansiColors.blue('[marketplace]'), `${extension.name}@${extension.version}`, ansiColors.green('✔︎')); + log(ansiColors.blue('[marketplace]'), `${extension.name}@${extension.version}`, ansiColors.green('✔︎')); return es.readArray([]); } @@ -53,13 +62,13 @@ function syncMarketplaceExtension(extension) { return ext.fromMarketplace(extension.name, extension.version, extension.metadata) .pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`)) .pipe(vfs.dest('.build/builtInExtensions')) - .on('end', () => fancyLog(ansiColors.blue('[marketplace]'), extension.name, ansiColors.green('✔︎'))); + .on('end', () => log(ansiColors.blue('[marketplace]'), extension.name, ansiColors.green('✔︎'))); } function syncExtension(extension, controlState) { switch (controlState) { case 'disabled': - fancyLog(ansiColors.blue('[disabled]'), ansiColors.gray(extension.name)); + log(ansiColors.blue('[disabled]'), ansiColors.gray(extension.name)); return es.readArray([]); case 'marketplace': @@ -67,15 +76,15 @@ function syncExtension(extension, controlState) { default: if (!fs.existsSync(controlState)) { - fancyLog(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`)); + log(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`)); return es.readArray([]); } else if (!fs.existsSync(path.join(controlState, 'package.json'))) { - fancyLog(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`)); + log(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`)); return es.readArray([]); } - fancyLog(ansiColors.blue('[local]'), `${extension.name}: ${ansiColors.cyan(controlState)}`, ansiColors.green('✔︎')); + log(ansiColors.blue('[local]'), `${extension.name}: ${ansiColors.cyan(controlState)}`, ansiColors.green('✔︎')); return es.readArray([]); } } @@ -93,14 +102,14 @@ function writeControlFile(control) { fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } -function main() { - fancyLog('Syncronizing built-in extensions...'); - fancyLog(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); +exports.getBuiltInExtensions = function getBuiltInExtensions() { + log('Syncronizing built-in extensions...'); + log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); const streams = []; - for (const extension of builtInExtensions) { + for (const extension of [...builtInExtensions, ...webBuiltInExtensions]) { let controlState = control[extension.name] || 'marketplace'; control[extension.name] = controlState; @@ -109,14 +118,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); + }); +}; + +if (require.main === module) { + exports.getBuiltInExtensions().then(() => process.exit(0)).catch(err => { + console.error(err); + process.exit(1); + }); } - -main(); diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 881e8ff6c7f0d..7d0c8d9b55ef0 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -4,6 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); +exports.bundle = void 0; const fs = require("fs"); const path = require("path"); const vm = require("vm"); diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 170e086b35b7c..07cf31655419e 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.watchTask = exports.compileTask = void 0; const es = require("event-stream"); const fs = require("fs"); const gulp = require("gulp"); @@ -17,6 +18,7 @@ const reporter_1 = require("./reporter"); const util = require("./util"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); +const os = require("os"); const watch = require('./watch'); const reporter = reporter_1.createReporter(); function getTypeScriptCompilerOptions(src) { @@ -44,7 +46,7 @@ function createCompile(src, build, emitError) { const input = es.through(); const output = input .pipe(utf8Filter) - .pipe(bom()) + .pipe(bom()) // this is required to preserve BOM in test files that loose it otherwise .pipe(utf8Filter.restore) .pipe(tsFilter) .pipe(util.loadSourcemaps()) @@ -68,6 +70,9 @@ function createCompile(src, build, emitError) { } function compileTask(src, out, build) { return function () { + if (os.totalmem() < 4000000000) { + 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/compilation.ts b/build/lib/compilation.ts index 6f37821c7750b..11a0c7f2da753 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -18,6 +18,7 @@ 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'); @@ -54,7 +55,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { const input = es.through(); const output = input .pipe(utf8Filter) - .pipe(bom()) + .pipe(bom()) // this is required to preserve BOM in test files that loose it otherwise .pipe(utf8Filter.restore) .pipe(tsFilter) .pipe(util.loadSourcemaps()) @@ -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 index c7c4cd9a69339..301a769506042 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.config = void 0; const fs = require("fs"); const path = require("path"); const vfs = require("vinyl-fs"); @@ -15,12 +16,6 @@ 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 { @@ -32,7 +27,7 @@ function darwinBundleDocumentType(extensions, icon) { }; } exports.config = { - version: getElectronVersion(), + version: util.getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', copyright: 'Copyright (C) 2019 Microsoft. All rights reserved', @@ -46,14 +41,14 @@ exports.config = { 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(["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"], 'resources/darwin/javascript.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'), @@ -69,7 +64,7 @@ exports.config = { 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(["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', @@ -87,7 +82,7 @@ function getElectron(arch) { return () => { const electronOpts = _.extend({}, exports.config, { platform: process.platform, - arch, + arch: arch === 'armhf' ? 'arm' : arch, ffmpegChromium: true, keepDefaultApp: true }); @@ -99,7 +94,7 @@ function getElectron(arch) { }; } async function main(arch = process.arch) { - 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/electron.ts b/build/lib/electron.ts index 90a25f4cac8d9..125759c581562 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -19,12 +19,6 @@ 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) { @@ -38,7 +32,7 @@ function darwinBundleDocumentType(extensions: string[], icon: string) { } export const config = { - version: getElectronVersion(), + version: util.getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', copyright: 'Copyright (C) 2019 Microsoft. All rights reserved', @@ -52,14 +46,14 @@ export const config = { 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(["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"], 'resources/darwin/javascript.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'), @@ -75,7 +69,7 @@ export const config = { 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(["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 new file mode 100644 index 0000000000000..0d508d1d00e37 --- /dev/null +++ b/build/lib/eslint/code-import-patterns.js @@ -0,0 +1,59 @@ +"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-import-patterns.ts b/build/lib/eslint/code-import-patterns.ts new file mode 100644 index 0000000000000..c3daadbf59a0d --- /dev/null +++ b/build/lib/eslint/code-import-patterns.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { join } from 'path'; +import * as minimatch from 'minimatch'; +import { createImportRuleListener } from './utils'; + +interface ImportPatternsConfig { + target: string; + restrictions: string | string[]; +} + +export = new class implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + 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: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + const configs = context.options; + + for (const config of configs) { + if (minimatch(context.getFilename(), config.target)) { + return createImportRuleListener((node, value) => this._checkImport(context, config, node, value)); + } + } + + return {}; + } + + private _checkImport(context: eslint.Rule.RuleContext, config: ImportPatternsConfig, node: TSESTree.Node, path: string) { + + // resolve relative paths + if (path[0] === '.') { + path = join(context.getFilename(), path); + } + + let restrictions: string[]; + 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 new file mode 100644 index 0000000000000..db591f789c72c --- /dev/null +++ b/build/lib/eslint/code-layering.js @@ -0,0 +1,68 @@ +"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-layering.ts b/build/lib/eslint/code-layering.ts new file mode 100644 index 0000000000000..cca72eeec71e4 --- /dev/null +++ b/build/lib/eslint/code-layering.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { join, dirname } from 'path'; +import { createImportRuleListener } from './utils'; + +type Config = { + allowed: Set; + disallowed: Set; +}; + +export = new class implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + 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: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + const fileDirname = dirname(context.getFilename()); + const parts = fileDirname.split(/\\|\//); + const ruleArgs = >context.options[0]; + + let config: Config | undefined; + 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 createImportRuleListener((node, path) => { + if (path[0] === '.') { + path = join(dirname(context.getFilename()), path); + } + + const parts = 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 new file mode 100644 index 0000000000000..d8955507bedc9 --- /dev/null +++ b/build/lib/eslint/code-no-nls-in-standalone-editor.js @@ -0,0 +1,38 @@ +"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-nls-in-standalone-editor.ts b/build/lib/eslint/code-no-nls-in-standalone-editor.ts new file mode 100644 index 0000000000000..90c80dee70c9d --- /dev/null +++ b/build/lib/eslint/code-no-nls-in-standalone-editor.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { join } from 'path'; +import { createImportRuleListener } from './utils'; + +export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + noNls: 'Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts' + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + 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 createImportRuleListener((node, path) => { + // resolve relative paths + if (path[0] === '.') { + path = 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 new file mode 100644 index 0000000000000..d9d6bb55b872f --- /dev/null +++ b/build/lib/eslint/code-no-standalone-editor.js @@ -0,0 +1,41 @@ +"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-standalone-editor.ts b/build/lib/eslint/code-no-standalone-editor.ts new file mode 100644 index 0000000000000..898886d17d203 --- /dev/null +++ b/build/lib/eslint/code-no-standalone-editor.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 * as eslint from 'eslint'; +import { join } from 'path'; +import { createImportRuleListener } from './utils'; + +export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + badImport: 'Not allowed to import standalone editor modules.' + }, + docs: { + url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + if (/vs(\/|\\)editor/.test(context.getFilename())) { + // the vs/editor folder is allowed to use the standalone editor + return {}; + } + + return createImportRuleListener((node, path) => { + + // resolve relative paths + if (path[0] === '.') { + path = 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 new file mode 100644 index 0000000000000..28fce5b9a182b --- /dev/null +++ b/build/lib/eslint/code-no-unexternalized-strings.js @@ -0,0 +1,111 @@ +"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 new file mode 100644 index 0000000000000..29db884cd9f31 --- /dev/null +++ b/build/lib/eslint/code-no-unexternalized-strings.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 eslint from 'eslint'; +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; + +function isStringLiteral(node: TSESTree.Node | null | undefined): node is TSESTree.StringLiteral { + return !!node && node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string'; +} + +function isDoubleQuoted(node: TSESTree.StringLiteral): boolean { + return node.raw[0] === '"' && node.raw[node.raw.length - 1] === '"'; +} + +export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { + + private static _rNlsKeys = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/; + + readonly meta: eslint.Rule.RuleMetaData = { + 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: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + const externalizedStringLiterals = new Map(); + const doubleQuotedStringLiterals = new Set(); + + function collectDoubleQuotedStrings(node: TSESTree.Literal) { + if (isStringLiteral(node) && isDoubleQuoted(node)) { + doubleQuotedStringLiterals.add(node); + } + } + + function visitLocalizeCall(node: TSESTree.CallExpression) { + + // localize(key, message) + const [keyNode, messageNode] = (node).arguments; + + // (1) + // extract key so that it can be checked later + let key: string | undefined; + if (isStringLiteral(keyNode)) { + doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider + key = keyNode.value; + + } else if (keyNode.type === AST_NODE_TYPES.ObjectExpression) { + for (let property of keyNode.properties) { + 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 + 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: any) => collectDoubleQuotedStrings(node), + ['ExpressionStatement[directive] Literal:exit']: (node: any) => doubleQuotedStringLiterals.delete(node), + ['CallExpression[callee.type="MemberExpression"][callee.object.name="nls"][callee.property.name="localize"]:exit']: (node: any) => visitLocalizeCall(node), + ['CallExpression[callee.name="localize"][arguments.length>=2]:exit']: (node: any) => visitLocalizeCall(node), + ['Program:exit']: reportBadStringsAndBadKeys, + }; + } +}; + diff --git a/build/lib/eslint/code-no-unused-expressions.js b/build/lib/eslint/code-no-unused-expressions.js new file mode 100644 index 0000000000000..c7b175513117c --- /dev/null +++ b/build/lib/eslint/code-no-unused-expressions.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +// FORKED FROM https://github.com/eslint/eslint/blob/b23ad0d789a909baf8d7c41a35bc53df932eaf30/lib/rules/no-unused-expressions.js +// and added support for `OptionalCallExpression`, see https://github.com/facebook/create-react-app/issues/8107 and https://github.com/eslint/eslint/issues/12642 + +/** + * @fileoverview Flag expressions in statement position that do not side effect + * @author Michael Ficarra + */ + +'use strict'; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'suggestion', + + docs: { + description: 'disallow unused expressions', + category: 'Best Practices', + recommended: false, + url: 'https://eslint.org/docs/rules/no-unused-expressions' + }, + + schema: [ + { + type: 'object', + properties: { + allowShortCircuit: { + type: 'boolean', + default: false + }, + allowTernary: { + type: 'boolean', + default: false + }, + allowTaggedTemplates: { + type: 'boolean', + default: false + } + }, + additionalProperties: false + } + ] + }, + + create(context) { + 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) { + 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) { + for (let i = 0; i < list.length; ++i) { + if (!predicate(list[i])) { + return list.slice(0, i); + } + } + return list.slice(); + } + + // 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) { + 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) { + const parent = ancestors[ancestors.length - 1], + grandparent = ancestors[ancestors.length - 2]; + + return (parent.type === 'Program' || parent.type === 'BlockStatement' && + (/Function/u.test(grandparent.type))) && + 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) { + if (allowTernary) { + + // Recursive check for ternary and logical expressions + if (node.type === 'ConditionalExpression') { + return isValidExpression(node.consequent) && isValidExpression(node.alternate); + } + } + + if (allowShortCircuit) { + if (node.type === 'LogicalExpression') { + return isValidExpression(node.right); + } + } + + if (allowTaggedTemplates && node.type === 'TaggedTemplateExpression') { + return true; + } + + return /^(?:Assignment|OptionalCall|Call|New|Update|Yield|Await)Expression$/u.test(node.type) || + (node.type === 'UnaryExpression' && ['delete', 'void'].indexOf(node.operator) >= 0); + } + + 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.' }); + } + } + }; + + } +}; diff --git a/build/lib/eslint/code-translation-remind.js b/build/lib/eslint/code-translation-remind.js new file mode 100644 index 0000000000000..a276e7c0028aa --- /dev/null +++ b/build/lib/eslint/code-translation-remind.js @@ -0,0 +1,57 @@ +"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/code-translation-remind.ts b/build/lib/eslint/code-translation-remind.ts new file mode 100644 index 0000000000000..1ce01107a72a5 --- /dev/null +++ b/build/lib/eslint/code-translation-remind.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { readFileSync } from 'fs'; +import { createImportRuleListener } from './utils'; + + +export = new class TranslationRemind implements eslint.Rule.RuleModule { + + private static NLS_MODULE = 'vs/nls'; + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + missing: 'Please add \'{{resource}}\' to ./build/lib/i18n.resources.json file to use translations here.' + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + return createImportRuleListener((node, path) => this._checkImport(context, node, path)); + } + + private _checkImport(context: eslint.Rule.RuleContext, node: TSESTree.Node, path: string) { + + 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 = 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: any) => { + if (existingResource.name === resource) { + resourceDefined = true; + return; + } + }); + + if (!resourceDefined) { + context.report({ + loc: node.loc, + messageId: 'missing', + data: { resource } + }); + } + } +}; + diff --git a/build/lib/eslint/utils.js b/build/lib/eslint/utils.js new file mode 100644 index 0000000000000..c58e4e24be1e1 --- /dev/null +++ b/build/lib/eslint/utils.js @@ -0,0 +1,37 @@ +"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 }); +exports.createImportRuleListener = void 0; +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/utils.ts b/build/lib/eslint/utils.ts new file mode 100644 index 0000000000000..428832e9cf97a --- /dev/null +++ b/build/lib/eslint/utils.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 } from '@typescript-eslint/experimental-utils'; + +export function createImportRuleListener(validateImport: (node: TSESTree.Literal, value: string) => any): eslint.Rule.RuleListener { + + function _checkImport(node: TSESTree.Node | null) { + if (node && node.type === 'Literal' && typeof node.value === 'string') { + validateImport(node, node.value); + } + } + + return { + // import ??? from 'module' + ImportDeclaration: (node: any) => { + _checkImport((node).source); + }, + // import('module').then(...) OR await import('module') + ['CallExpression[callee.type="Import"][arguments.length=1] > Literal']: (node: any) => { + _checkImport(node); + }, + // import foo = ... + ['TSImportEqualsDeclaration > TSExternalModuleReference > Literal']: (node: any) => { + _checkImport(node); + }, + // export ?? from 'module' + ExportAllDeclaration: (node: any) => { + _checkImport((node).source); + }, + // export {foo} from 'module' + ExportNamedDeclaration: (node: any) => { + _checkImport((node).source); + }, + + }; +} diff --git a/build/lib/eslint/vscode-dts-create-func.js b/build/lib/eslint/vscode-dts-create-func.js new file mode 100644 index 0000000000000..5a27bf51c80f8 --- /dev/null +++ b/build/lib/eslint/vscode-dts-create-func.js @@ -0,0 +1,35 @@ +"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 experimental_utils_1 = require("@typescript-eslint/experimental-utils"); +module.exports = new class ApiLiteralOrTypes { + constructor() { + this.meta = { + 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) { + return { + ['TSDeclareFunction Identifier[name=/create.*/]']: (node) => { + var _a; + const decl = node.parent; + if (((_a = decl.returnType) === null || _a === void 0 ? void 0 : _a.typeAnnotation.type) !== experimental_utils_1.AST_NODE_TYPES.TSTypeReference) { + return; + } + if (decl.returnType.typeAnnotation.typeName.type !== experimental_utils_1.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-create-func.ts b/build/lib/eslint/vscode-dts-create-func.ts new file mode 100644 index 0000000000000..295d099da7ce4 --- /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.js b/build/lib/eslint/vscode-dts-event-naming.js new file mode 100644 index 0000000000000..388ccf2f80401 --- /dev/null +++ b/build/lib/eslint/vscode-dts-event-naming.js @@ -0,0 +1,87 @@ +"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"); +module.exports = new (_a = class ApiEventNaming { + constructor() { + this.meta = { + 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) { + const config = context.options[0]; + const allowed = new Set(config.allowed); + const verbs = new Set(config.verbs); + return { + ['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node) => { + var _a, _b; + const def = (_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.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] } + }); + } + } + } + }; + } + getIdent(def) { + if (!def) { + return; + } + if (def.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { + return def; + } + else if ((def.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || def.type === experimental_utils_1.AST_NODE_TYPES.ClassProperty) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { + return def.key; + } + return this.getIdent(def.parent); + } + }, + _a._nameRegExp = /on(Did|Will)([A-Z][a-z]+)([A-Z][a-z]+)?/, + _a); 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 0000000000000..5ed8818fe44b8 --- /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.js b/build/lib/eslint/vscode-dts-interface-naming.js new file mode 100644 index 0000000000000..70ca810825ba0 --- /dev/null +++ b/build/lib/eslint/vscode-dts-interface-naming.js @@ -0,0 +1,30 @@ +"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; +module.exports = new (_a = class ApiInterfaceNaming { + constructor() { + this.meta = { + messages: { + naming: 'Interfaces must not be prefixed with uppercase `I`', + } + }; + } + create(context) { + return { + ['TSInterfaceDeclaration Identifier']: (node) => { + const name = node.name; + if (ApiInterfaceNaming._nameRegExp.test(name)) { + context.report({ + node, + messageId: 'naming' + }); + } + } + }; + } + }, + _a._nameRegExp = /I[A-Z]/, + _a); 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 0000000000000..d9ec4e8c34cbe --- /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.js b/build/lib/eslint/vscode-dts-literal-or-types.js new file mode 100644 index 0000000000000..e07dfc6de28fe --- /dev/null +++ b/build/lib/eslint/vscode-dts-literal-or-types.js @@ -0,0 +1,27 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +module.exports = new class ApiLiteralOrTypes { + constructor() { + this.meta = { + docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#enums' }, + messages: { useEnum: 'Use enums, not literal-or-types', } + }; + } + create(context) { + return { + ['TSTypeAnnotation TSUnionType TSLiteralType']: (node) => { + var _a; + if (((_a = node.literal) === null || _a === void 0 ? void 0 : _a.type) === 'TSNullKeyword') { + return; + } + context.report({ + node: node, + messageId: 'useEnum' + }); + } + }; + } +}; 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 0000000000000..fe4befd84e794 --- /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 index c4385655eb929..fe0deffc6d0c1 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -4,6 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); +exports.translatePackageJSON = exports.scanBuiltinExtensions = exports.packageMarketplaceExtensionsStream = exports.packageLocalExtensionsStream = exports.fromMarketplace = void 0; const es = require("event-stream"); const fs = require("fs"); const glob = require("glob"); @@ -21,33 +22,66 @@ const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); const buffer = require('gulp-buffer'); const json = require("gulp-json-editor"); +const jsoncParser = require("jsonc-parser"); 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 }); +function minifyExtensionResources(input) { + const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true }); + return input + .pipe(jsonFilter) + .pipe(buffer()) + .pipe(es.mapSync((f) => { + const errors = []; + 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(jsonFilter.restore); +} +function updateExtensionPackageJSON(input, update) { + const packageJsonFilter = filter('extensions/*/package.json', { restore: true }); return input - .pipe(tmLanguageJsonFilter) + .pipe(packageJsonFilter) .pipe(buffer()) .pipe(es.mapSync((f) => { - f.contents = Buffer.from(JSON.stringify(JSON.parse(f.contents.toString('utf8')))); + const data = JSON.parse(f.contents.toString('utf8')); + f.contents = Buffer.from(JSON.stringify(update(data))); return f; })) - .pipe(tmLanguageJsonFilter.restore); + .pipe(packageJsonFilter.restore); } -function fromLocalWebpack(extensionPath) { +function fromLocal(extensionPath, forWeb) { + 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) => { + 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, webpackConfigFileName) { 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')); + const webpackRootConfig = require(path.join(extensionPath, webpackConfigFileName)); for (const key in webpackRootConfig.externals) { if (key in packageJsonConfig.dependencies) { packagedDependencies.push(key); @@ -63,30 +97,9 @@ function fromLocalWebpack(extensionPath) { 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); + // and merge its output with the files stream. + const webpackConfigLocations = glob.sync(path.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] }); const webpackStreams = webpackConfigLocations.map(webpackConfigPath => { const webpackDone = (err, stats) => { fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); @@ -120,7 +133,7 @@ function fromLocalWebpack(extensionPath) { this.emit('data', data); })); }); - es.merge(...webpackStreams, patchFilesStream) + es.merge(...webpackStreams, es.readArray(files)) // .pipe(es.through(function (data) { // // debug // console.log('out', data.path, data.contents.length); @@ -184,32 +197,129 @@ const excludedExtensions = [ 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', + 'vscode-notebook-tests', + 'vscode-custom-editor-tests', ]; -const builtInExtensions = require('../builtInExtensions.json'); -function packageLocalExtensionsStream() { - const localExtensionDescriptions = glob.sync('extensions/*/package.json') +const marketplaceWebExtensions = [ + 'ms-vscode.references-view' +]; +const productJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); +const builtInExtensions = productJson.builtInExtensions || []; +const webBuiltInExtensions = productJson.webBuiltInExtensions || []; +/** + * Loosely based on `getExtensionKind` from `src/vs/workbench/services/extensions/common/extensionsUtil.ts` + */ +function isWebExtension(manifest) { + 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)); +} +function packageLocalExtensionsStream(forWeb) { + 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 }; + return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; }) .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) + .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(nodeModules, ...localExtensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); + }))); + let result; + 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']))); } exports.packageLocalExtensionsStream = packageLocalExtensionsStream; -function packageMarketplaceExtensionsStream() { - const extensions = builtInExtensions.map(extension => { - return fromMarketplace(extension.name, extension.version, extension.metadata) +function packageMarketplaceExtensionsStream(forWeb) { + 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 es.merge(extensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); + return updateExtensionPackageJSON(input, (data) => { + delete data.scripts; + delete data.dependencies; + delete data.devDependencies; + return data; + }); + }))); + return (marketplaceExtensionsStream + .pipe(util2.setExecutableBit(['**/*.sh']))); } exports.packageMarketplaceExtensionsStream = packageMarketplaceExtensionsStream; +function scanBuiltinExtensions(extensionsRoot, exclude = []) { + const scannedExtensions = []; + 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; + } +} +exports.scanBuiltinExtensions = scanBuiltinExtensions; +function translatePackageJSON(packageJSON, packageNLSPath) { + const CharCode_PC = '%'.charCodeAt(0); + const packageNls = JSON.parse(fs.readFileSync(packageNLSPath).toString()); + const translate = (obj) => { + 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; +} +exports.translatePackageJSON = translatePackageJSON; diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 05bc70948468f..dac71c814798e 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(jsonFilter) + .pipe(buffer()) + .pipe(es.mapSync((f: File) => { + 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(jsonFilter.restore); +} +function updateExtensionPackageJSON(input: Stream, update: (data: any) => any): Stream { + const packageJsonFilter = filter('extensions/*/package.json', { restore: true }); return input - .pipe(tmLanguageJsonFilter) + .pipe(packageJsonFilter) .pipe(buffer()) .pipe(es.mapSync((f: File) => { - f.contents = Buffer.from(JSON.stringify(JSON.parse(f.contents.toString('utf8')))); + const data = JSON.parse(f.contents.toString('utf8')); + f.contents = Buffer.from(JSON.stringify(update(data))); return f; })) - .pipe(tmLanguageJsonFilter.restore); + .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): Stream { + +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)); +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)); +} - 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}`)); - }); +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}`)); + }) + ) + ); + + let result: Stream; + if (forWeb) { + result = localExtensionsStream; + } else { + // also include shared node modules + result = es.merge(localExtensionsStream, gulp.src('extensions/node_modules/**', { base: '.' })); + } - return es.merge(nodeModules, ...localExtensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); + return ( + result + .pipe(util2.setExecutableBit(['**/*.sh'])) + ); } -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 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; +} - return es.merge(extensions) - .pipe(util2.setExecutableBit(['**/*.sh'])); +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 index da5d66fd8d222..1726f76fcc791 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.getVersion = void 0; const path = require("path"); const fs = require("fs"); /** diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 27a4054a1e4d0..7371b0f022f8a 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -4,6 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); +exports.prepareIslFiles = exports.prepareI18nPackFiles = exports.pullI18nPackFiles = exports.prepareI18nFiles = exports.pullSetupXlfFiles = exports.pullCoreAndExtensionsXlfFiles = exports.findObsoleteResources = exports.pushXlfFiles = exports.createXlfFilesForIsl = exports.createXlfFilesForExtensions = exports.createXlfFilesForCoreBundle = exports.getResource = exports.processNlsFiles = exports.Limiter = exports.XLF = exports.Line = exports.externalExtensionsWithTranslations = exports.extraLanguages = exports.defaultLanguages = void 0; const path = require("path"); const fs = require("fs"); const event_stream_1 = require("event-stream"); @@ -15,7 +16,7 @@ const https = require("https"); const gulp = require("gulp"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); -const iconv = require("iconv-lite"); +const iconv = require("iconv-lite-umd"); const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; function log(message, ...rest) { fancyLog(ansiColors.green('[i18n]'), message, ...rest); @@ -112,7 +113,7 @@ 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); } @@ -152,9 +153,12 @@ class XLF { 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)}`); + addStringItem(file, item) { + 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); this.appendNewLine(`${item.message}`, 6); @@ -1137,12 +1141,7 @@ function createIslFile(originalFilePath, messages, language, innoSetup) { 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 = line.split('='); @@ -1171,9 +1170,10 @@ function createIslFile(originalFilePath, messages, language, innoSetup) { }); 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), }); } function encodeEntities(value) { diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 2e2a20d89adbf..a192a31e57dae 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -30,6 +30,14 @@ "name": "vs/workbench/api/common", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/backup", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/bulkEdit", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/cli", "project": "vscode-workbench" @@ -58,6 +66,10 @@ "name": "vs/workbench/contrib/emmet", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/experiments", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/extensions", "project": "vscode-workbench" @@ -82,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" @@ -107,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" }, { @@ -122,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" @@ -130,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" @@ -139,7 +167,7 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/stats", + "name": "vs/workbench/contrib/tags", "project": "vscode-workbench" }, { @@ -194,10 +222,18 @@ "name": "vs/workbench/contrib/userDataSync", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/views", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/actions", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/authToken", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/bulkEdit", "project": "vscode-workbench" @@ -214,10 +250,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" @@ -238,6 +270,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" @@ -262,6 +298,10 @@ "name": "vs/workbench/services/remote", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/search", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/textfile", "project": "vscode-workbench" @@ -274,6 +314,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" @@ -297,6 +341,22 @@ { "name": "vs/workbench/services/userData", "project": "vscode-workbench" + }, + { + "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 d69109a9d05b4..418a7b5c362ab 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, @@ -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 new file mode 100644 index 0000000000000..864c47f6ca3a0 --- /dev/null +++ b/build/lib/layersChecker.js @@ -0,0 +1,274 @@ +"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', + 'assert', + 'Error', + 'String', + 'throws', + 'stack', + 'captureStackTrace', + 'stackTraceLimit', + 'TextDecoder', + 'TextEncoder', + 'encode', + 'decode', + 'self', + 'trimLeft', + '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' +]; +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' + ], + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', + '@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', + '@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', + '@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', + '@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' + ], + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', + '@types/node' // no node.js + ] + }, + // Common + { + target: '**/vs/**/common/**', + allowedTypes: CORE_TYPES, + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', + '@types/node' // no node.js + ] + }, + // Browser + { + 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 + ] + }, + // 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 (sandbox) + { + target: '**/vs/**/electron-sandbox/**', + allowedTypes: CORE_TYPES, + disallowedDefinitions: [ + '@types/node' // no node.js + ] + }, + // 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, _b; + 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 + } + if ((_b = rule.disallowedTypes) === null || _b === void 0 ? void 0 : _b.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) { + 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 new file mode 100644 index 0000000000000..5d620c79db038 --- /dev/null +++ b/build/lib/layersChecker.ts @@ -0,0 +1,319 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import { readFileSync, existsSync } from 'fs'; +import { resolve, dirname, join } from 'path'; +import { match } from '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', // from our AMD loader + 'atob', + 'btoa', + 'setTimeout', + 'clearTimeout', + 'setInterval', + 'clearInterval', + 'console', + 'log', + 'info', + 'warn', + 'error', + 'group', + 'groupEnd', + 'table', + 'assert', + 'Error', + 'String', + 'throws', + 'stack', + 'captureStackTrace', + 'stackTraceLimit', + 'TextDecoder', + 'TextEncoder', + 'encode', + 'decode', + 'self', + 'trimLeft', + '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' +]; + +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' + ], + 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/workbench/api/common/extHostExtensionService.ts + { + target: '**/vs/workbench/api/common/extHostExtensionService.ts', + allowedTypes: [ + ...CORE_TYPES, + + // Safe access to global + 'global' + ], + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + + // Common + { + target: '**/vs/**/common/**', + allowedTypes: CORE_TYPES, + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + + // Browser + { + 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 + ] + }, + + // 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 (sandbox) + { + target: '**/vs/**/electron-sandbox/**', + allowedTypes: CORE_TYPES, + disallowedDefinitions: [ + '@types/node' // no node.js + ] + }, + + // 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 = join(__dirname, '../../', 'src', 'tsconfig.json'); + +interface IRule { + target: string; + skip?: boolean; + allowedTypes?: string[]; + disallowedDefinitions?: string[]; + disallowedTypes?: string[]; +} + +let hasErrors = false; + +function checkFile(program: ts.Program, sourceFile: ts.SourceFile, rule: IRule) { + checkNode(sourceFile); + + function checkNode(node: ts.Node): void { + if (node.kind !== ts.SyntaxKind.Identifier) { + return ts.forEachChild(node, checkNode); // recurse down + } + + const text = node.getText(sourceFile); + + if (rule.allowedTypes?.some(allowed => allowed === text)) { + 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) { + 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: string): ts.Program { + const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + + const configHostParser: ts.ParseConfigHost = { fileExists: existsSync, readDirectory: ts.sys.readDirectory, readFile: file => readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' }; + const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, resolve(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 (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/optimize.js b/build/lib/optimize.js index 45f1169846301..5403c105620a1 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.minifyTask = exports.optimizeTask = exports.loaderConfig = void 0; const es = require("event-stream"); -const fs = require("fs"); const gulp = require("gulp"); const concat = require("gulp-concat"); const minifyCSS = require("gulp-cssnano"); @@ -28,13 +28,13 @@ const REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(prefix, message) { fancyLog(ansiColors.cyan('[' + prefix + ']'), message); } -function loaderConfig(emptyPaths) { +function loaderConfig() { const result = { paths: { 'vs': 'out-build/vs', 'vscode': 'empty:' }, - nodeModules: emptyPaths || [] + amdModulesPattern: /^vs\// }; result['vs/css'] = { inlineResources: true }; return result; @@ -68,14 +68,9 @@ function loader(src, bundledFileHeader, bundleLoader) { 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; - }))); + .pipe(concat('vs/loader.js'))); } -function toConcatStream(src, bundledFileHeader, sources, dest) { +function toConcatStream(src, bundledFileHeader, sources, dest, fileContentMapper) { 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 @@ -96,10 +91,12 @@ function toConcatStream(src, bundledFileHeader, sources, dest) { const treatedSources = sources.map(function (source) { const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; 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) }); }); return es.readArray(treatedSources) @@ -107,9 +104,9 @@ function toConcatStream(src, bundledFileHeader, sources, dest) { .pipe(concat(dest)) .pipe(stats_1.createStatsStream(dest)); } -function toBundleStream(src, bundledFileHeader, bundles) { +function toBundleStream(src, bundledFileHeader, bundles, fileContentMapper) { return es.merge(bundles.map(function (bundle) { - return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest); + return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest, fileContentMapper); })); } const DEFAULT_FILE_HEADER = [ @@ -125,6 +122,7 @@ function optimizeTask(opts) { 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, _path) => contents); return function () { const bundlesStream = es.through(); // this stream will contain the bundled files const resourcesStream = es.through(); // this stream will contain the resources @@ -133,15 +131,7 @@ function optimizeTask(opts) { 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(); result.cssInlinedResources.forEach(function (resource) { @@ -176,39 +166,6 @@ function optimizeTask(opts) { }; } 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. diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 46189514c30a1..2822803f9f7a0 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` @@ -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 @@ -114,11 +103,13 @@ 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 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.js b/build/lib/preLaunch.js new file mode 100644 index 0000000000000..1aecbe190487e --- /dev/null +++ b/build/lib/preLaunch.js @@ -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. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +// @ts-check +const path = require("path"); +const child_process_1 = require("child_process"); +const fs_1 = require("fs"); +const yarn = process.platform === 'win32' ? 'yarn.cmd' : 'yarn'; +const rootDir = path.resolve(__dirname, '..', '..'); +function runProcess(command, args = []) { + return new Promise((resolve, reject) => { + const child = child_process_1.spawn(command, args, { cwd: rootDir, stdio: 'inherit', env: process.env }); + child.on('exit', err => !err ? resolve() : process.exit(err !== null && err !== void 0 ? err : 1)); + child.on('error', reject); + }); +} +async function exists(subdir) { + try { + await fs_1.promises.stat(path.join(rootDir, subdir)); + return true; + } + catch (_a) { + 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/preLaunch.ts b/build/lib/preLaunch.ts new file mode 100644 index 0000000000000..57441870cc4d9 --- /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 index e0461dc6d9d38..67615bf48dc46 100644 --- a/build/lib/reporter.js +++ b/build/lib/reporter.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.createReporter = void 0; const es = require("event-stream"); const _ = require("underscore"); const fancyLog = require("fancy-log"); diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 56f89544dac98..531194c35fdcd 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -4,6 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); +exports.createESMSourcesAndResources2 = exports.extractEditor = void 0; const ts = require("typescript"); const fs = require("fs"); const path = require("path"); @@ -43,7 +44,9 @@ function extractEditor(options) { compilerOptions.declaration = false; compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; options.compilerOptions = compilerOptions; - console.log(`Running with shakeLevel ${tss.toStringShakeLevel(options.shakeLevel)}`); + 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)) { diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 07000331ad2b6..f80c611e6fc7b 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -50,7 +50,10 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str options.compilerOptions = compilerOptions; - console.log(`Running with shakeLevel ${tss.toStringShakeLevel(options.shakeLevel)}`); + 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) { diff --git a/build/lib/stats.js b/build/lib/stats.js index 99ad665f22320..2ff02e405a68a 100644 --- a/build/lib/stats.js +++ b/build/lib/stats.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.submitAllStats = exports.createStatsStream = void 0; const es = require("event-stream"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); diff --git a/build/lib/task.js b/build/lib/task.js index f1e6e3f6245de..d08ab8acde8f0 100644 --- a/build/lib/task.js +++ b/build/lib/task.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.define = exports.parallel = exports.series = void 0; const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); function _isPromise(p) { diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index 50ff7caa01d3b..5b1cf0591eca9 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.shake = exports.toStringShakeLevel = exports.ShakeLevel = void 0; const fs = require("fs"); const path = require("path"); const ts = require("typescript"); @@ -25,17 +26,17 @@ function toStringShakeLevel(shakeLevel) { } } exports.toStringShakeLevel = toStringShakeLevel; -function printDiagnostics(diagnostics) { +function printDiagnostics(options, diagnostics) { for (const diag of diagnostics) { let result = ''; if (diag.file) { - result += `${diag.file.fileName}: `; + 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 += `:${location.line + 1}:${location.character}`; } - result += JSON.stringify(diag.messageText); + result += ` - ` + JSON.stringify(diag.messageText); console.log(result); } } @@ -44,17 +45,17 @@ function shake(options) { const program = languageService.getProgram(); const globalDiagnostics = program.getGlobalDiagnostics(); if (globalDiagnostics.length > 0) { - printDiagnostics(globalDiagnostics); + printDiagnostics(options, globalDiagnostics); throw new Error(`Compilation Errors encountered.`); } const syntacticDiagnostics = program.getSyntacticDiagnostics(); if (syntacticDiagnostics.length > 0) { - printDiagnostics(syntacticDiagnostics); + printDiagnostics(options, syntacticDiagnostics); throw new Error(`Compilation Errors encountered.`); } const semanticDiagnostics = program.getSemanticDiagnostics(); if (semanticDiagnostics.length > 0) { - printDiagnostics(semanticDiagnostics); + printDiagnostics(options, semanticDiagnostics); throw new Error(`Compilation Errors encountered.`); } markNodes(languageService, options); @@ -75,11 +76,7 @@ function createTypeScriptLanguageService(options) { 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 RESOLVED_LIBS = processLibFiles(options); const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions); return ts.createLanguageService(host); @@ -137,6 +134,29 @@ function discoverAndReadFiles(options) { } return FILES; } +/** + * Read lib files and follow lib references + */ +function processLibFiles(options) { + const stack = [...options.compilerOptions.lib]; + const result = {}; + 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; +} /** * A TypeScript language service host */ @@ -234,6 +254,7 @@ function markNodes(languageService, options) { } const black_queue = []; const gray_queue = []; + const export_import_queue = []; const sourceFilesLoaded = {}; function enqueueTopLevelModuleStatements(sourceFile) { sourceFile.forEachChild((node) => { @@ -245,10 +266,16 @@ function markNodes(languageService, options) { return; } 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, 2 /* 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; } if (ts.isExpressionStatement(node) @@ -306,7 +333,7 @@ function markNodes(languageService, options) { } 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))) { + if (options.shakeLevel === 2 /* 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++) { @@ -358,7 +385,7 @@ function markNodes(languageService, options) { ++step; let node; if (step % 100 === 0) { - console.log(`${step}/${step + black_queue.length + gray_queue.length} (${black_queue.length}, ${gray_queue.length})`); + 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++) { @@ -393,7 +420,7 @@ function markNodes(languageService, options) { // (they can be the declaration of a module import) continue; } - if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { + if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration)) { enqueue_black(declaration.name); for (let j = 0; j < declaration.members.length; j++) { const member = declaration.members[j]; @@ -402,6 +429,8 @@ function markNodes(languageService, options) { || 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 @@ -426,6 +455,22 @@ function markNodes(languageService, options) { }; node.forEachChild(loop); } + while (export_import_queue.length > 0) { + const node = export_import_queue.shift(); + if (nodeOrParentIsBlack(node)) { + continue; + } + const symbol = 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, 2 /* Black */); + } + } + } } function nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol) { for (let i = 0, len = symbol.declarations.length; i < len; i++) { @@ -517,6 +562,21 @@ function generateResult(languageService, shakeLevel) { } } } + if (ts.isExportDeclaration(node)) { + if (node.exportClause && node.moduleSpecifier && ts.isNamedExports(node.exportClause)) { + let survivingExports = []; + for (const exportSpecifier of node.exportClause.elements) { + if (getColor(exportSpecifier) === 2 /* 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 === 2 /* ClassMembers */ && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) { let toWrite = node.getFullText(); for (let i = node.members.length - 1; i >= 0; i--) { @@ -554,6 +614,34 @@ function generateResult(languageService, shakeLevel) { } //#endregion //#region Utils +function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration) { + 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, type) { + 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) */ @@ -567,7 +655,7 @@ function getRealNodeSymbol(checker, node) { // (2) when the aliased symbol is originating from an import. // function shouldSkipAlias(node, declaration) { - if (node.kind !== ts.SyntaxKind.Identifier) { + if (!ts.isShorthandPropertyAssignment(node) && node.kind !== ts.SyntaxKind.Identifier) { return false; } if (node.parent === declaration) { @@ -589,7 +677,9 @@ function getRealNodeSymbol(checker, node) { } } const { parent } = node; - let symbol = checker.getSymbolAtLocation(node); + let symbol = (ts.isShorthandPropertyAssignment(node) + ? checker.getShorthandAssignmentValueSymbol(node) + : 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. diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index f096963365582..405336bfcbb0a 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 */ @@ -71,17 +66,17 @@ export interface ITreeShakingResult { [file: string]: string; } -function printDiagnostics(diagnostics: ReadonlyArray): void { +function printDiagnostics(options: ITreeShakingOptions, diagnostics: ReadonlyArray): void { for (const diag of diagnostics) { let result = ''; if (diag.file) { - result += `${diag.file.fileName}: `; + 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 += `:${location.line + 1}:${location.character}`; } - result += JSON.stringify(diag.messageText); + result += ` - ` + JSON.stringify(diag.messageText); console.log(result); } } @@ -92,19 +87,19 @@ export function shake(options: ITreeShakingOptions): ITreeShakingResult { const globalDiagnostics = program.getGlobalDiagnostics(); if (globalDiagnostics.length > 0) { - printDiagnostics(globalDiagnostics); + printDiagnostics(options, globalDiagnostics); throw new Error(`Compilation Errors encountered.`); } const syntacticDiagnostics = program.getSyntacticDiagnostics(); if (syntacticDiagnostics.length > 0) { - printDiagnostics(syntacticDiagnostics); + printDiagnostics(options, syntacticDiagnostics); throw new Error(`Compilation Errors encountered.`); } const semanticDiagnostics = program.getSemanticDiagnostics(); if (semanticDiagnostics.length > 0) { - printDiagnostics(semanticDiagnostics); + printDiagnostics(options, semanticDiagnostics); throw new Error(`Compilation Errors encountered.`); } @@ -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++) { @@ -471,11 +497,11 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt let node: ts.Node; if (step % 100 === 0) { - console.log(`${step}/${step + black_queue.length + gray_queue.length} (${black_queue.length}, ${gray_queue.length})`); + 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++) { + 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/tslint/abstractGlobalsRule.js b/build/lib/tslint/abstractGlobalsRule.js deleted file mode 100644 index 1566c0aa57605..0000000000000 --- a/build/lib/tslint/abstractGlobalsRule.js +++ /dev/null @@ -1,45 +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 Lint = require("tslint"); -class AbstractGlobalsRuleWalker extends Lint.RuleWalker { - constructor(file, program, opts, _config) { - super(file, opts); - this.program = program; - this._config = _config; - } - visitIdentifier(node) { - if (this.getDisallowedGlobals().some(disallowedGlobal => disallowedGlobal === node.text)) { - if (this._config.allowed && this._config.allowed.some(allowed => allowed === node.text)) { - return; // override - } - const checker = this.program.getTypeChecker(); - const symbol = checker.getSymbolAtLocation(node); - if (symbol) { - const declarations = symbol.declarations; - if (Array.isArray(declarations) && symbol.declarations.some(declaration => { - if (declaration) { - const parent = declaration.parent; - if (parent) { - const sourceFile = parent.getSourceFile(); - if (sourceFile) { - const fileName = sourceFile.fileName; - if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { - return true; - } - } - } - } - return false; - })) { - this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); - } - } - } - super.visitIdentifier(node); - } -} -exports.AbstractGlobalsRuleWalker = AbstractGlobalsRuleWalker; diff --git a/build/lib/tslint/abstractGlobalsRule.ts b/build/lib/tslint/abstractGlobalsRule.ts deleted file mode 100644 index 543720455c3b7..0000000000000 --- a/build/lib/tslint/abstractGlobalsRule.ts +++ /dev/null @@ -1,57 +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 ts from 'typescript'; -import * as Lint from 'tslint'; - -interface AbstractGlobalsRuleConfig { - target: string; - allowed: string[]; -} - -export abstract class AbstractGlobalsRuleWalker extends Lint.RuleWalker { - - constructor(file: ts.SourceFile, private program: ts.Program, opts: Lint.IOptions, private _config: AbstractGlobalsRuleConfig) { - super(file, opts); - } - - protected abstract getDisallowedGlobals(): string[]; - - protected abstract getDefinitionPattern(): string; - - visitIdentifier(node: ts.Identifier) { - if (this.getDisallowedGlobals().some(disallowedGlobal => disallowedGlobal === node.text)) { - if (this._config.allowed && this._config.allowed.some(allowed => allowed === node.text)) { - return; // override - } - - const checker = this.program.getTypeChecker(); - const symbol = checker.getSymbolAtLocation(node); - if (symbol) { - const declarations = symbol.declarations; - if (Array.isArray(declarations) && symbol.declarations.some(declaration => { - if (declaration) { - const parent = declaration.parent; - if (parent) { - const sourceFile = parent.getSourceFile(); - if (sourceFile) { - const fileName = sourceFile.fileName; - if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) { - return true; - } - } - } - } - - return false; - })) { - this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`); - } - } - } - - super.visitIdentifier(node); - } -} diff --git a/build/lib/tslint/duplicateImportsRule.js b/build/lib/tslint/duplicateImportsRule.js deleted file mode 100644 index f336095fc6a20..0000000000000 --- a/build/lib/tslint/duplicateImportsRule.js +++ /dev/null @@ -1,32 +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_1 = require("path"); -const Lint = require("tslint"); -class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile) { - return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions())); - } -} -exports.Rule = Rule; -class ImportPatterns extends Lint.RuleWalker { - constructor(file, opts) { - super(file, opts); - this.imports = Object.create(null); - } - visitImportDeclaration(node) { - let path = node.moduleSpecifier.getText(); - // remove quotes - path = path.slice(1, -1); - if (path[0] === '.') { - path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path); - } - if (this.imports[path]) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Duplicate imports for '${path}'.`)); - } - this.imports[path] = true; - } -} diff --git a/build/lib/tslint/duplicateImportsRule.ts b/build/lib/tslint/duplicateImportsRule.ts deleted file mode 100644 index c648084be1db0..0000000000000 --- a/build/lib/tslint/duplicateImportsRule.ts +++ /dev/null @@ -1,40 +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 ts from 'typescript'; -import { join, dirname } from 'path'; -import * as Lint from 'tslint'; - -export class Rule extends Lint.Rules.AbstractRule { - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions())); - } -} - -class ImportPatterns extends Lint.RuleWalker { - - private imports: { [path: string]: boolean; } = Object.create(null); - - constructor(file: ts.SourceFile, opts: Lint.IOptions) { - super(file, opts); - } - - protected visitImportDeclaration(node: ts.ImportDeclaration): void { - let path = node.moduleSpecifier.getText(); - - // remove quotes - path = path.slice(1, -1); - - if (path[0] === '.') { - path = join(dirname(node.getSourceFile().fileName), path); - } - - if (this.imports[path]) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Duplicate imports for '${path}'.`)); - } - - this.imports[path] = true; - } -} diff --git a/build/lib/tslint/importPatternsRule.js b/build/lib/tslint/importPatternsRule.js deleted file mode 100644 index a64f5a6967877..0000000000000 --- a/build/lib/tslint/importPatternsRule.js +++ /dev/null @@ -1,70 +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 Lint = require("tslint"); -const minimatch = require("minimatch"); -const path_1 = require("path"); -class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile) { - const configs = this.getOptions().ruleArguments; - for (const config of configs) { - if (minimatch(sourceFile.fileName, config.target)) { - return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config)); - } - } - return []; - } -} -exports.Rule = Rule; -class ImportPatterns extends Lint.RuleWalker { - constructor(file, opts, _config) { - super(file, opts); - this._config = _config; - } - visitImportEqualsDeclaration(node) { - if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - this._validateImport(node.moduleReference.expression.getText(), node); - } - } - visitImportDeclaration(node) { - this._validateImport(node.moduleSpecifier.getText(), node); - } - visitCallExpression(node) { - super.visitCallExpression(node); - // import('foo') statements inside the code - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { - const [path] = node.arguments; - this._validateImport(path.getText(), node); - } - } - _validateImport(path, node) { - // remove quotes - path = path.slice(1, -1); - // resolve relative paths - if (path[0] === '.') { - path = path_1.join(this.getSourceFile().fileName, path); - } - let restrictions; - if (typeof this._config.restrictions === 'string') { - restrictions = [this._config.restrictions]; - } - else { - restrictions = this._config.restrictions; - } - let matched = false; - for (const pattern of restrictions) { - if (minimatch(path, pattern)) { - matched = true; - break; - } - } - if (!matched) { - // None of the restrictions matched - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Imports violates '${restrictions.join(' or ')}' restrictions. See https://github.com/Microsoft/vscode/wiki/Code-Organization`)); - } - } -} diff --git a/build/lib/tslint/importPatternsRule.ts b/build/lib/tslint/importPatternsRule.ts deleted file mode 100644 index 6e545a6be1605..0000000000000 --- a/build/lib/tslint/importPatternsRule.ts +++ /dev/null @@ -1,87 +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 ts from 'typescript'; -import * as Lint from 'tslint'; -import * as minimatch from 'minimatch'; -import { join } from 'path'; - -interface ImportPatternsConfig { - target: string; - restrictions: string | string[]; -} - -export class Rule extends Lint.Rules.AbstractRule { - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - - const configs = this.getOptions().ruleArguments; - - - for (const config of configs) { - if (minimatch(sourceFile.fileName, config.target)) { - return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config)); - } - } - - return []; - } -} - -class ImportPatterns extends Lint.RuleWalker { - - constructor(file: ts.SourceFile, opts: Lint.IOptions, private _config: ImportPatternsConfig) { - super(file, opts); - } - - protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { - if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - this._validateImport(node.moduleReference.expression.getText(), node); - } - } - - protected visitImportDeclaration(node: ts.ImportDeclaration): void { - this._validateImport(node.moduleSpecifier.getText(), node); - } - - protected visitCallExpression(node: ts.CallExpression): void { - super.visitCallExpression(node); - - // import('foo') statements inside the code - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { - const [path] = node.arguments; - this._validateImport(path.getText(), node); - } - } - - private _validateImport(path: string, node: ts.Node): void { - // remove quotes - path = path.slice(1, -1); - - // resolve relative paths - if (path[0] === '.') { - path = join(this.getSourceFile().fileName, path); - } - - let restrictions: string[]; - if (typeof this._config.restrictions === 'string') { - restrictions = [this._config.restrictions]; - } else { - restrictions = this._config.restrictions; - } - - let matched = false; - for (const pattern of restrictions) { - if (minimatch(path, pattern)) { - matched = true; - break; - } - } - - if (!matched) { - // None of the restrictions matched - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Imports violates '${restrictions.join(' or ')}' restrictions. See https://github.com/Microsoft/vscode/wiki/Code-Organization`)); - } - } -} diff --git a/build/lib/tslint/layeringRule.js b/build/lib/tslint/layeringRule.js deleted file mode 100644 index 21a7c9a16d947..0000000000000 --- a/build/lib/tslint/layeringRule.js +++ /dev/null @@ -1,83 +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 Lint = require("tslint"); -const path_1 = require("path"); -class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile) { - const parts = path_1.dirname(sourceFile.fileName).split(/\\|\//); - const ruleArgs = this.getOptions().ruleArguments[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) { - return []; - } - return this.applyWithWalker(new LayeringRule(sourceFile, config, this.getOptions())); - } -} -exports.Rule = Rule; -class LayeringRule extends Lint.RuleWalker { - constructor(file, config, opts) { - super(file, opts); - this._config = config; - } - visitImportEqualsDeclaration(node) { - if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - this._validateImport(node.moduleReference.expression.getText(), node); - } - } - visitImportDeclaration(node) { - this._validateImport(node.moduleSpecifier.getText(), node); - } - visitCallExpression(node) { - super.visitCallExpression(node); - // import('foo') statements inside the code - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { - const [path] = node.arguments; - this._validateImport(path.getText(), node); - } - } - _validateImport(path, node) { - // remove quotes - path = path.slice(1, -1); - if (path[0] === '.') { - path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path); - } - const parts = path_1.dirname(path).split(/\\|\//); - for (let i = parts.length - 1; i >= 0; i--) { - const part = parts[i]; - if (this._config.allowed.has(part)) { - // GOOD - same layer - return; - } - if (this._config.disallowed.has(part)) { - // BAD - wrong layer - const message = `Bad layering. You are not allowed to access '${part}' from here, allowed layers are: [${LayeringRule._print(this._config.allowed)}]`; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); - return; - } - } - } - static _print(set) { - const r = []; - set.forEach(e => r.push(e)); - return r.join(', '); - } -} diff --git a/build/lib/tslint/layeringRule.ts b/build/lib/tslint/layeringRule.ts deleted file mode 100644 index dc0053fe8de17..0000000000000 --- a/build/lib/tslint/layeringRule.ts +++ /dev/null @@ -1,105 +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 ts from 'typescript'; -import * as Lint from 'tslint'; -import { join, dirname } from 'path'; - -interface Config { - allowed: Set; - disallowed: Set; -} - -export class Rule extends Lint.Rules.AbstractRule { - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - - const parts = dirname(sourceFile.fileName).split(/\\|\//); - const ruleArgs = this.getOptions().ruleArguments[0]; - - let config: Config | undefined; - 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) { - return []; - } - - return this.applyWithWalker(new LayeringRule(sourceFile, config, this.getOptions())); - } -} - -class LayeringRule extends Lint.RuleWalker { - - private _config: Config; - - constructor(file: ts.SourceFile, config: Config, opts: Lint.IOptions) { - super(file, opts); - this._config = config; - } - - protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { - if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - this._validateImport(node.moduleReference.expression.getText(), node); - } - } - - protected visitImportDeclaration(node: ts.ImportDeclaration): void { - this._validateImport(node.moduleSpecifier.getText(), node); - } - - protected visitCallExpression(node: ts.CallExpression): void { - super.visitCallExpression(node); - - // import('foo') statements inside the code - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { - const [path] = node.arguments; - this._validateImport(path.getText(), node); - } - } - - private _validateImport(path: string, node: ts.Node): void { - // remove quotes - path = path.slice(1, -1); - - if (path[0] === '.') { - path = join(dirname(node.getSourceFile().fileName), path); - } - - const parts = dirname(path).split(/\\|\//); - for (let i = parts.length - 1; i >= 0; i--) { - const part = parts[i]; - - if (this._config.allowed.has(part)) { - // GOOD - same layer - return; - } - - if (this._config.disallowed.has(part)) { - // BAD - wrong layer - const message = `Bad layering. You are not allowed to access '${part}' from here, allowed layers are: [${LayeringRule._print(this._config.allowed)}]`; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message)); - return; - } - } - } - - static _print(set: Set): string { - const r: string[] = []; - set.forEach(e => r.push(e)); - return r.join(', '); - } -} diff --git a/build/lib/tslint/noDomGlobalsRule.js b/build/lib/tslint/noDomGlobalsRule.js deleted file mode 100644 index a83ac8f7f5958..0000000000000 --- a/build/lib/tslint/noDomGlobalsRule.js +++ /dev/null @@ -1,34 +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 Lint = require("tslint"); -const minimatch = require("minimatch"); -const abstractGlobalsRule_1 = require("./abstractGlobalsRule"); -class Rule extends Lint.Rules.TypedRule { - applyWithProgram(sourceFile, program) { - const configs = this.getOptions().ruleArguments; - for (const config of configs) { - if (minimatch(sourceFile.fileName, config.target)) { - return this.applyWithWalker(new NoDOMGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); - } - } - return []; - } -} -exports.Rule = Rule; -class NoDOMGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRuleWalker { - getDefinitionPattern() { - return 'lib.dom.d.ts'; - } - getDisallowedGlobals() { - // intentionally not complete - return [ - "window", - "document", - "HTMLElement" - ]; - } -} diff --git a/build/lib/tslint/noDomGlobalsRule.ts b/build/lib/tslint/noDomGlobalsRule.ts deleted file mode 100644 index df9e67bf78b6f..0000000000000 --- a/build/lib/tslint/noDomGlobalsRule.ts +++ /dev/null @@ -1,45 +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 ts from 'typescript'; -import * as Lint from 'tslint'; -import * as minimatch from 'minimatch'; -import { AbstractGlobalsRuleWalker } from './abstractGlobalsRule'; - -interface NoDOMGlobalsRuleConfig { - target: string; - allowed: string[]; -} - -export class Rule extends Lint.Rules.TypedRule { - - applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { - const configs = this.getOptions().ruleArguments; - - for (const config of configs) { - if (minimatch(sourceFile.fileName, config.target)) { - return this.applyWithWalker(new NoDOMGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); - } - } - - return []; - } -} - -class NoDOMGlobalsRuleWalker extends AbstractGlobalsRuleWalker { - - getDefinitionPattern(): string { - return 'lib.dom.d.ts'; - } - - getDisallowedGlobals(): string[] { - // intentionally not complete - return [ - "window", - "document", - "HTMLElement" - ]; - } -} diff --git a/build/lib/tslint/noNewBufferRule.js b/build/lib/tslint/noNewBufferRule.js deleted file mode 100644 index ae9b02d457ec4..0000000000000 --- a/build/lib/tslint/noNewBufferRule.js +++ /dev/null @@ -1,22 +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 Lint = require("tslint"); -class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile) { - return this.applyWithWalker(new NewBufferRuleWalker(sourceFile, this.getOptions())); - } -} -exports.Rule = Rule; -class NewBufferRuleWalker extends Lint.RuleWalker { - visitNewExpression(node) { - if (node.expression.kind === ts.SyntaxKind.Identifier && node.expression && node.expression.text === 'Buffer') { - this.addFailureAtNode(node, '`new Buffer` is deprecated. Consider Buffer.From or Buffer.alloc instead.'); - } - super.visitNewExpression(node); - } -} diff --git a/build/lib/tslint/noNewBufferRule.ts b/build/lib/tslint/noNewBufferRule.ts deleted file mode 100644 index 7b0dd43e5383a..0000000000000 --- a/build/lib/tslint/noNewBufferRule.ts +++ /dev/null @@ -1,23 +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 ts from 'typescript'; -import * as Lint from 'tslint'; - -export class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new NewBufferRuleWalker(sourceFile, this.getOptions())); - } -} - -class NewBufferRuleWalker extends Lint.RuleWalker { - visitNewExpression(node: ts.NewExpression) { - if (node.expression.kind === ts.SyntaxKind.Identifier && node.expression && (node.expression as ts.Identifier).text === 'Buffer') { - this.addFailureAtNode(node, '`new Buffer` is deprecated. Consider Buffer.From or Buffer.alloc instead.'); - } - - super.visitNewExpression(node); - } -} \ No newline at end of file diff --git a/build/lib/tslint/noNlsInStandaloneEditorRule.js b/build/lib/tslint/noNlsInStandaloneEditorRule.js deleted file mode 100644 index affd4cc837034..0000000000000 --- a/build/lib/tslint/noNlsInStandaloneEditorRule.js +++ /dev/null @@ -1,54 +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 Lint = require("tslint"); -const path_1 = require("path"); -class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile) { - if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName) - || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName) - || /vs(\/|\\)editor(\/|\\)editor.api/.test(sourceFile.fileName) - || /vs(\/|\\)editor(\/|\\)editor.main/.test(sourceFile.fileName) - || /vs(\/|\\)editor(\/|\\)editor.worker/.test(sourceFile.fileName)) { - return this.applyWithWalker(new NoNlsInStandaloneEditorRuleWalker(sourceFile, this.getOptions())); - } - return []; - } -} -exports.Rule = Rule; -class NoNlsInStandaloneEditorRuleWalker extends Lint.RuleWalker { - constructor(file, opts) { - super(file, opts); - } - visitImportEqualsDeclaration(node) { - if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - this._validateImport(node.moduleReference.expression.getText(), node); - } - } - visitImportDeclaration(node) { - this._validateImport(node.moduleSpecifier.getText(), node); - } - visitCallExpression(node) { - super.visitCallExpression(node); - // import('foo') statements inside the code - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { - const [path] = node.arguments; - this._validateImport(path.getText(), node); - } - } - _validateImport(path, node) { - // remove quotes - path = path.slice(1, -1); - // resolve relative paths - if (path[0] === '.') { - path = path_1.join(this.getSourceFile().fileName, path); - } - if (/vs(\/|\\)nls/.test(path)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts`)); - } - } -} diff --git a/build/lib/tslint/noNlsInStandaloneEditorRule.ts b/build/lib/tslint/noNlsInStandaloneEditorRule.ts deleted file mode 100644 index ae23d74d784d5..0000000000000 --- a/build/lib/tslint/noNlsInStandaloneEditorRule.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. - *--------------------------------------------------------------------------------------------*/ - -import * as ts from 'typescript'; -import * as Lint from 'tslint'; -import { join } from 'path'; - -export class Rule extends Lint.Rules.AbstractRule { - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - if ( - /vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName) - || /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName) - || /vs(\/|\\)editor(\/|\\)editor.api/.test(sourceFile.fileName) - || /vs(\/|\\)editor(\/|\\)editor.main/.test(sourceFile.fileName) - || /vs(\/|\\)editor(\/|\\)editor.worker/.test(sourceFile.fileName) - ) { - return this.applyWithWalker(new NoNlsInStandaloneEditorRuleWalker(sourceFile, this.getOptions())); - } - - return []; - } -} - -class NoNlsInStandaloneEditorRuleWalker extends Lint.RuleWalker { - - constructor(file: ts.SourceFile, opts: Lint.IOptions) { - super(file, opts); - } - - protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { - if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - this._validateImport(node.moduleReference.expression.getText(), node); - } - } - - protected visitImportDeclaration(node: ts.ImportDeclaration): void { - this._validateImport(node.moduleSpecifier.getText(), node); - } - - protected visitCallExpression(node: ts.CallExpression): void { - super.visitCallExpression(node); - - // import('foo') statements inside the code - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { - const [path] = node.arguments; - this._validateImport(path.getText(), node); - } - } - - private _validateImport(path: string, node: ts.Node): void { - // remove quotes - path = path.slice(1, -1); - - // resolve relative paths - if (path[0] === '.') { - path = join(this.getSourceFile().fileName, path); - } - - if ( - /vs(\/|\\)nls/.test(path) - ) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts`)); - } - } -} diff --git a/build/lib/tslint/noNodejsGlobalsRule.js b/build/lib/tslint/noNodejsGlobalsRule.js deleted file mode 100644 index 8c36fa342c273..0000000000000 --- a/build/lib/tslint/noNodejsGlobalsRule.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. - *--------------------------------------------------------------------------------------------*/ -Object.defineProperty(exports, "__esModule", { value: true }); -const Lint = require("tslint"); -const minimatch = require("minimatch"); -const abstractGlobalsRule_1 = require("./abstractGlobalsRule"); -class Rule extends Lint.Rules.TypedRule { - applyWithProgram(sourceFile, program) { - const configs = this.getOptions().ruleArguments; - for (const config of configs) { - if (minimatch(sourceFile.fileName, config.target)) { - return this.applyWithWalker(new NoNodejsGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); - } - } - return []; - } -} -exports.Rule = Rule; -class NoNodejsGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRuleWalker { - getDefinitionPattern() { - return '@types/node'; - } - getDisallowedGlobals() { - // https://nodejs.org/api/globals.html#globals_global_objects - return [ - "NodeJS", - "Buffer", - "__dirname", - "__filename", - "clearImmediate", - "exports", - "global", - "module", - "process", - "setImmediate" - ]; - } -} diff --git a/build/lib/tslint/noNodejsGlobalsRule.ts b/build/lib/tslint/noNodejsGlobalsRule.ts deleted file mode 100644 index 7e5767d857086..0000000000000 --- a/build/lib/tslint/noNodejsGlobalsRule.ts +++ /dev/null @@ -1,52 +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 ts from 'typescript'; -import * as Lint from 'tslint'; -import * as minimatch from 'minimatch'; -import { AbstractGlobalsRuleWalker } from './abstractGlobalsRule'; - -interface NoNodejsGlobalsConfig { - target: string; - allowed: string[]; -} - -export class Rule extends Lint.Rules.TypedRule { - - applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] { - const configs = this.getOptions().ruleArguments; - - for (const config of configs) { - if (minimatch(sourceFile.fileName, config.target)) { - return this.applyWithWalker(new NoNodejsGlobalsRuleWalker(sourceFile, program, this.getOptions(), config)); - } - } - - return []; - } -} - -class NoNodejsGlobalsRuleWalker extends AbstractGlobalsRuleWalker { - - getDefinitionPattern(): string { - return '@types/node'; - } - - getDisallowedGlobals(): string[] { - // https://nodejs.org/api/globals.html#globals_global_objects - return [ - "NodeJS", - "Buffer", - "__dirname", - "__filename", - "clearImmediate", - "exports", - "global", - "module", - "process", - "setImmediate" - ]; - } -} diff --git a/build/lib/tslint/noStandaloneEditorRule.js b/build/lib/tslint/noStandaloneEditorRule.js deleted file mode 100644 index 44b70a6a8d7bb..0000000000000 --- a/build/lib/tslint/noStandaloneEditorRule.js +++ /dev/null @@ -1,55 +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 Lint = require("tslint"); -const path_1 = require("path"); -class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile) { - if (/vs(\/|\\)editor/.test(sourceFile.fileName)) { - // the vs/editor folder is allowed to use the standalone editor - return []; - } - return this.applyWithWalker(new NoStandaloneEditorRuleWalker(sourceFile, this.getOptions())); - } -} -exports.Rule = Rule; -class NoStandaloneEditorRuleWalker extends Lint.RuleWalker { - constructor(file, opts) { - super(file, opts); - } - visitImportEqualsDeclaration(node) { - if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - this._validateImport(node.moduleReference.expression.getText(), node); - } - } - visitImportDeclaration(node) { - this._validateImport(node.moduleSpecifier.getText(), node); - } - visitCallExpression(node) { - super.visitCallExpression(node); - // import('foo') statements inside the code - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { - const [path] = node.arguments; - this._validateImport(path.getText(), node); - } - } - _validateImport(path, node) { - // remove quotes - path = path.slice(1, -1); - // resolve relative paths - if (path[0] === '.') { - path = path_1.join(this.getSourceFile().fileName, 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)) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import standalone editor modules. See https://github.com/Microsoft/vscode/wiki/Code-Organization`)); - } - } -} diff --git a/build/lib/tslint/noStandaloneEditorRule.ts b/build/lib/tslint/noStandaloneEditorRule.ts deleted file mode 100644 index 713b94097d4c0..0000000000000 --- a/build/lib/tslint/noStandaloneEditorRule.ts +++ /dev/null @@ -1,65 +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 ts from 'typescript'; -import * as Lint from 'tslint'; -import { join } from 'path'; - -export class Rule extends Lint.Rules.AbstractRule { - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - if (/vs(\/|\\)editor/.test(sourceFile.fileName)) { - // the vs/editor folder is allowed to use the standalone editor - return []; - } - return this.applyWithWalker(new NoStandaloneEditorRuleWalker(sourceFile, this.getOptions())); - } -} - -class NoStandaloneEditorRuleWalker extends Lint.RuleWalker { - - constructor(file: ts.SourceFile, opts: Lint.IOptions) { - super(file, opts); - } - - protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { - if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) { - this._validateImport(node.moduleReference.expression.getText(), node); - } - } - - protected visitImportDeclaration(node: ts.ImportDeclaration): void { - this._validateImport(node.moduleSpecifier.getText(), node); - } - - protected visitCallExpression(node: ts.CallExpression): void { - super.visitCallExpression(node); - - // import('foo') statements inside the code - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { - const [path] = node.arguments; - this._validateImport(path.getText(), node); - } - } - - private _validateImport(path: string, node: ts.Node): void { - // remove quotes - path = path.slice(1, -1); - - // resolve relative paths - if (path[0] === '.') { - path = join(this.getSourceFile().fileName, 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) - ) { - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import standalone editor modules. See https://github.com/Microsoft/vscode/wiki/Code-Organization`)); - } - } -} diff --git a/build/lib/tslint/noUnexternalizedStringsRule.js b/build/lib/tslint/noUnexternalizedStringsRule.js deleted file mode 100644 index a1183ce68e767..0000000000000 --- a/build/lib/tslint/noUnexternalizedStringsRule.js +++ /dev/null @@ -1,183 +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 Lint = require("tslint"); -/** - * Implementation of the no-unexternalized-strings rule. - */ -class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile) { - if (/\.d.ts$/.test(sourceFile.fileName)) { - return []; - } - return this.applyWithWalker(new NoUnexternalizedStringsRuleWalker(sourceFile, this.getOptions())); - } -} -exports.Rule = Rule; -function isStringLiteral(node) { - return node && node.kind === ts.SyntaxKind.StringLiteral; -} -function isObjectLiteral(node) { - return node && node.kind === ts.SyntaxKind.ObjectLiteralExpression; -} -function isPropertyAssignment(node) { - return node && node.kind === ts.SyntaxKind.PropertyAssignment; -} -class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { - constructor(file, opts) { - super(file, opts); - this.signatures = Object.create(null); - this.ignores = Object.create(null); - this.messageIndex = undefined; - this.keyIndex = undefined; - this.usedKeys = Object.create(null); - const options = this.getOptions(); - const first = options && options.length > 0 ? options[0] : null; - if (first) { - if (Array.isArray(first.signatures)) { - first.signatures.forEach((signature) => this.signatures[signature] = true); - } - if (Array.isArray(first.ignores)) { - first.ignores.forEach((ignore) => this.ignores[ignore] = true); - } - if (typeof first.messageIndex !== 'undefined') { - this.messageIndex = first.messageIndex; - } - if (typeof first.keyIndex !== 'undefined') { - this.keyIndex = first.keyIndex; - } - } - } - visitSourceFile(node) { - super.visitSourceFile(node); - Object.keys(this.usedKeys).forEach(key => { - // Keys are quoted. - let identifier = key.substr(1, key.length - 2); - if (!NoUnexternalizedStringsRuleWalker.IDENTIFIER.test(identifier)) { - let occurrence = this.usedKeys[key][0]; - this.addFailure(this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `The key ${occurrence.key.getText()} doesn't conform to a valid localize identifier`)); - } - const occurrences = this.usedKeys[key]; - if (occurrences.length > 1) { - occurrences.forEach(occurrence => { - this.addFailure((this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `Duplicate key ${occurrence.key.getText()} with different message value.`))); - }); - } - }); - } - visitStringLiteral(node) { - this.checkStringLiteral(node); - super.visitStringLiteral(node); - } - checkStringLiteral(node) { - const text = node.getText(); - const doubleQuoted = text.length >= 2 && text[0] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE && text[text.length - 1] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE; - const info = this.findDescribingParent(node); - // Ignore strings in import and export nodes. - if (info && info.isImport && doubleQuoted) { - const fix = [ - Lint.Replacement.replaceFromTo(node.getStart(), 1, '\''), - Lint.Replacement.replaceFromTo(node.getStart() + text.length - 1, 1, '\''), - ]; - this.addFailureAtNode(node, NoUnexternalizedStringsRuleWalker.ImportFailureMessage, fix); - return; - } - const callInfo = info ? info.callInfo : null; - const functionName = callInfo ? callInfo.callExpression.expression.getText() : null; - if (functionName && this.ignores[functionName]) { - return; - } - if (doubleQuoted && (!callInfo || callInfo.argIndex === -1 || !this.signatures[functionName])) { - const s = node.getText(); - const fix = [ - Lint.Replacement.replaceFromTo(node.getStart(), node.getWidth(), `nls.localize('KEY-${s.substring(1, s.length - 1)}', ${s})`), - ]; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Unexternalized string found: ${node.getText()}`, fix)); - return; - } - // We have a single quoted string outside a localize function name. - if (!doubleQuoted && !this.signatures[functionName]) { - return; - } - // We have a string that is a direct argument into the localize call. - const keyArg = callInfo && callInfo.argIndex === this.keyIndex - ? callInfo.callExpression.arguments[this.keyIndex] - : null; - if (keyArg) { - if (isStringLiteral(keyArg)) { - this.recordKey(keyArg, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined); - } - else if (isObjectLiteral(keyArg)) { - for (const property of keyArg.properties) { - if (isPropertyAssignment(property)) { - const name = property.name.getText(); - if (name === 'key') { - const initializer = property.initializer; - if (isStringLiteral(initializer)) { - this.recordKey(initializer, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined); - } - break; - } - } - } - } - } - const messageArg = callInfo.callExpression.arguments[this.messageIndex]; - if (messageArg && messageArg.kind !== ts.SyntaxKind.StringLiteral) { - this.addFailure(this.createFailure(messageArg.getStart(), messageArg.getWidth(), `Message argument to '${callInfo.callExpression.expression.getText()}' must be a string literal.`)); - return; - } - } - recordKey(keyNode, messageNode) { - const text = keyNode.getText(); - // We have an empty key - if (text.match(/(['"]) *\1/)) { - if (messageNode) { - this.addFailureAtNode(keyNode, `Key is empty for message: ${messageNode.getText()}`); - } - else { - this.addFailureAtNode(keyNode, `Key is empty.`); - } - return; - } - let occurrences = this.usedKeys[text]; - if (!occurrences) { - occurrences = []; - this.usedKeys[text] = occurrences; - } - if (messageNode) { - if (occurrences.some(pair => pair.message ? pair.message.getText() === messageNode.getText() : false)) { - return; - } - } - occurrences.push({ key: keyNode, message: messageNode }); - } - findDescribingParent(node) { - let parent; - while ((parent = node.parent)) { - const kind = parent.kind; - if (kind === ts.SyntaxKind.CallExpression) { - const callExpression = parent; - return { callInfo: { callExpression: callExpression, argIndex: callExpression.arguments.indexOf(node) } }; - } - else if (kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportDeclaration || kind === ts.SyntaxKind.ExportDeclaration) { - return { isImport: true }; - } - else if (kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.PropertyDeclaration - || kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.VariableDeclarationList || kind === ts.SyntaxKind.InterfaceDeclaration - || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ModuleDeclaration - || kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.SourceFile) { - return null; - } - node = parent; - } - return null; - } -} -NoUnexternalizedStringsRuleWalker.ImportFailureMessage = 'Do not use double quotes for imports.'; -NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE = '"'; -NoUnexternalizedStringsRuleWalker.IDENTIFIER = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/; diff --git a/build/lib/tslint/noUnexternalizedStringsRule.ts b/build/lib/tslint/noUnexternalizedStringsRule.ts deleted file mode 100644 index d896e4fabe2ee..0000000000000 --- a/build/lib/tslint/noUnexternalizedStringsRule.ts +++ /dev/null @@ -1,222 +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 ts from 'typescript'; -import * as Lint from 'tslint'; - -/** - * Implementation of the no-unexternalized-strings rule. - */ -export class Rule extends Lint.Rules.AbstractRule { - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - if (/\.d.ts$/.test(sourceFile.fileName)) { - return []; - } - return this.applyWithWalker(new NoUnexternalizedStringsRuleWalker(sourceFile, this.getOptions())); - } -} - -interface Map { - [key: string]: V; -} - -interface UnexternalizedStringsOptions { - signatures?: string[]; - messageIndex?: number; - keyIndex?: number; - ignores?: string[]; -} - -function isStringLiteral(node: ts.Node): node is ts.StringLiteral { - return node && node.kind === ts.SyntaxKind.StringLiteral; -} - -function isObjectLiteral(node: ts.Node): node is ts.ObjectLiteralExpression { - return node && node.kind === ts.SyntaxKind.ObjectLiteralExpression; -} - -function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment { - return node && node.kind === ts.SyntaxKind.PropertyAssignment; -} - -interface KeyMessagePair { - key: ts.StringLiteral; - message: ts.Node | undefined; -} - -class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker { - - private static ImportFailureMessage = 'Do not use double quotes for imports.'; - - private static DOUBLE_QUOTE: string = '"'; - - private signatures: Map; - private messageIndex: number | undefined; - private keyIndex: number | undefined; - private ignores: Map; - - private usedKeys: Map; - - constructor(file: ts.SourceFile, opts: Lint.IOptions) { - super(file, opts); - this.signatures = Object.create(null); - this.ignores = Object.create(null); - this.messageIndex = undefined; - this.keyIndex = undefined; - this.usedKeys = Object.create(null); - const options: any[] = this.getOptions(); - const first: UnexternalizedStringsOptions = options && options.length > 0 ? options[0] : null; - if (first) { - if (Array.isArray(first.signatures)) { - first.signatures.forEach((signature: string) => this.signatures[signature] = true); - } - if (Array.isArray(first.ignores)) { - first.ignores.forEach((ignore: string) => this.ignores[ignore] = true); - } - if (typeof first.messageIndex !== 'undefined') { - this.messageIndex = first.messageIndex; - } - if (typeof first.keyIndex !== 'undefined') { - this.keyIndex = first.keyIndex; - } - } - } - - private static IDENTIFIER = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/; - protected visitSourceFile(node: ts.SourceFile): void { - super.visitSourceFile(node); - Object.keys(this.usedKeys).forEach(key => { - // Keys are quoted. - let identifier = key.substr(1, key.length - 2); - if (!NoUnexternalizedStringsRuleWalker.IDENTIFIER.test(identifier)) { - let occurrence = this.usedKeys[key][0]; - this.addFailure(this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `The key ${occurrence.key.getText()} doesn't conform to a valid localize identifier`)); - } - const occurrences = this.usedKeys[key]; - if (occurrences.length > 1) { - occurrences.forEach(occurrence => { - this.addFailure((this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `Duplicate key ${occurrence.key.getText()} with different message value.`))); - }); - } - }); - } - - protected visitStringLiteral(node: ts.StringLiteral): void { - this.checkStringLiteral(node); - super.visitStringLiteral(node); - } - - private checkStringLiteral(node: ts.StringLiteral): void { - const text = node.getText(); - const doubleQuoted = text.length >= 2 && text[0] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE && text[text.length - 1] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE; - const info = this.findDescribingParent(node); - // Ignore strings in import and export nodes. - if (info && info.isImport && doubleQuoted) { - const fix = [ - Lint.Replacement.replaceFromTo(node.getStart(), 1, '\''), - Lint.Replacement.replaceFromTo(node.getStart() + text.length - 1, 1, '\''), - ]; - this.addFailureAtNode( - node, - NoUnexternalizedStringsRuleWalker.ImportFailureMessage, - fix - ); - return; - } - const callInfo = info ? info.callInfo : null; - const functionName = callInfo ? callInfo.callExpression.expression.getText() : null; - if (functionName && this.ignores[functionName]) { - return; - } - - if (doubleQuoted && (!callInfo || callInfo.argIndex === -1 || !this.signatures[functionName!])) { - const s = node.getText(); - const fix = [ - Lint.Replacement.replaceFromTo(node.getStart(), node.getWidth(), `nls.localize('KEY-${s.substring(1, s.length - 1)}', ${s})`), - ]; - this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Unexternalized string found: ${node.getText()}`, fix)); - return; - } - // We have a single quoted string outside a localize function name. - if (!doubleQuoted && !this.signatures[functionName!]) { - return; - } - // We have a string that is a direct argument into the localize call. - const keyArg: ts.Expression | null = callInfo && callInfo.argIndex === this.keyIndex - ? callInfo.callExpression.arguments[this.keyIndex] - : null; - if (keyArg) { - if (isStringLiteral(keyArg)) { - this.recordKey(keyArg, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined); - } else if (isObjectLiteral(keyArg)) { - for (const property of keyArg.properties) { - if (isPropertyAssignment(property)) { - const name = property.name.getText(); - if (name === 'key') { - const initializer = property.initializer; - if (isStringLiteral(initializer)) { - this.recordKey(initializer, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined); - } - break; - } - } - } - } - } - - const messageArg = callInfo!.callExpression.arguments[this.messageIndex!]; - - if (messageArg && messageArg.kind !== ts.SyntaxKind.StringLiteral) { - this.addFailure(this.createFailure( - messageArg.getStart(), messageArg.getWidth(), - `Message argument to '${callInfo!.callExpression.expression.getText()}' must be a string literal.`)); - return; - } - } - - private recordKey(keyNode: ts.StringLiteral, messageNode: ts.Node | undefined) { - const text = keyNode.getText(); - // We have an empty key - if (text.match(/(['"]) *\1/)) { - if (messageNode) { - this.addFailureAtNode(keyNode, `Key is empty for message: ${messageNode.getText()}`); - } else { - this.addFailureAtNode(keyNode, `Key is empty.`); - } - return; - } - let occurrences: KeyMessagePair[] = this.usedKeys[text]; - if (!occurrences) { - occurrences = []; - this.usedKeys[text] = occurrences; - } - if (messageNode) { - if (occurrences.some(pair => pair.message ? pair.message.getText() === messageNode.getText() : false)) { - return; - } - } - occurrences.push({ key: keyNode, message: messageNode }); - } - - private findDescribingParent(node: ts.Node): { callInfo?: { callExpression: ts.CallExpression, argIndex: number }, isImport?: boolean; } | null { - let parent: ts.Node; - while ((parent = node.parent)) { - const kind = parent.kind; - if (kind === ts.SyntaxKind.CallExpression) { - const callExpression = parent as ts.CallExpression; - return { callInfo: { callExpression: callExpression, argIndex: callExpression.arguments.indexOf(node) } }; - } else if (kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportDeclaration || kind === ts.SyntaxKind.ExportDeclaration) { - return { isImport: true }; - } else if (kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.PropertyDeclaration - || kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.VariableDeclarationList || kind === ts.SyntaxKind.InterfaceDeclaration - || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ModuleDeclaration - || kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.SourceFile) { - return null; - } - node = parent; - } - return null; - } -} diff --git a/build/lib/tslint/translationRemindRule.js b/build/lib/tslint/translationRemindRule.js deleted file mode 100644 index 2234094421bb9..0000000000000 --- a/build/lib/tslint/translationRemindRule.js +++ /dev/null @@ -1,62 +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 Lint = require("tslint"); -const fs = require("fs"); -class Rule extends Lint.Rules.AbstractRule { - apply(sourceFile) { - return this.applyWithWalker(new TranslationRemindRuleWalker(sourceFile, this.getOptions())); - } -} -exports.Rule = Rule; -class TranslationRemindRuleWalker extends Lint.RuleWalker { - constructor(file, opts) { - super(file, opts); - } - visitImportDeclaration(node) { - const declaration = node.moduleSpecifier.getText(); - if (declaration !== `'${TranslationRemindRuleWalker.NLS_MODULE}'`) { - return; - } - this.visitImportLikeDeclaration(node); - } - visitImportEqualsDeclaration(node) { - const reference = node.moduleReference.getText(); - if (reference !== `require('${TranslationRemindRuleWalker.NLS_MODULE}')`) { - return; - } - this.visitImportLikeDeclaration(node); - } - visitImportLikeDeclaration(node) { - const currentFile = node.getSourceFile().fileName; - 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.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) { - this.addFailureAtNode(node, `Please add '${resource}' to ./build/lib/i18n.resources.json file to use translations here.`); - } - } -} -TranslationRemindRuleWalker.NLS_MODULE = 'vs/nls'; diff --git a/build/lib/tslint/translationRemindRule.ts b/build/lib/tslint/translationRemindRule.ts deleted file mode 100644 index e2671599d3c0a..0000000000000 --- a/build/lib/tslint/translationRemindRule.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 ts from 'typescript'; -import * as Lint from 'tslint'; -import * as fs from 'fs'; - -export class Rule extends Lint.Rules.AbstractRule { - public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { - return this.applyWithWalker(new TranslationRemindRuleWalker(sourceFile, this.getOptions())); - } -} - -class TranslationRemindRuleWalker extends Lint.RuleWalker { - - private static NLS_MODULE: string = 'vs/nls'; - - constructor(file: ts.SourceFile, opts: Lint.IOptions) { - super(file, opts); - } - - protected visitImportDeclaration(node: ts.ImportDeclaration): void { - const declaration = node.moduleSpecifier.getText(); - if (declaration !== `'${TranslationRemindRuleWalker.NLS_MODULE}'`) { - return; - } - - this.visitImportLikeDeclaration(node); - } - - protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void { - const reference = node.moduleReference.getText(); - if (reference !== `require('${TranslationRemindRuleWalker.NLS_MODULE}')`) { - return; - } - - this.visitImportLikeDeclaration(node); - } - - private visitImportLikeDeclaration(node: ts.ImportDeclaration | ts.ImportEqualsDeclaration) { - const currentFile = node.getSourceFile().fileName; - 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.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: any) => { - if (existingResource.name === resource) { - resourceDefined = true; - return; - } - }); - - if (!resourceDefined) { - this.addFailureAtNode(node, `Please add '${resource}' to ./build/lib/i18n.resources.json file to use translations here.`); - } - } -} diff --git a/build/lib/util.js b/build/lib/util.js index 1888bfed2e448..e552a036f89bd 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.getElectronVersion = exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.getVersion = exports.ensureDir = exports.rreddir = exports.rimraf = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.incremental = void 0; const es = require("event-stream"); const debounce = require("debounce"); const _filter = require("gulp-filter"); @@ -13,6 +14,7 @@ const fs = require("fs"); const _rimraf = require("rimraf"); const git = require("./git"); const VinylFile = require("vinyl"); +const root = path.dirname(path.dirname(__dirname)); const NoCancellationToken = { isCancellationRequested: () => false }; function incremental(streamProvider, initial, supportsCancellation) { const input = es.through(); @@ -136,7 +138,7 @@ function loadSourcemaps() { version: '3', names: [], mappings: '', - sources: [f.relative.replace(/\//g, '/')], + sources: [f.relative], sourcesContent: [contents] }; cb(undefined, f); @@ -185,6 +187,31 @@ function rimraf(dir) { 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)) { @@ -229,3 +256,9 @@ function streamToPromise(stream) { }); } exports.streamToPromise = streamToPromise; +function getElectronVersion() { + const yarnrc = fs.readFileSync(path.join(root, '.yarnrc'), 'utf8'); + const target = /^target "(.*)"$/m.exec(yarnrc)[1]; + return target; +} +exports.getElectronVersion = getElectronVersion; diff --git a/build/lib/util.ts b/build/lib/util.ts index ae598d5f2cfd0..035c7e95ea300 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] }; @@ -243,6 +245,31 @@ export function rimraf(dir: string): () => Promise { return result; } +function _rreaddir(dirPath: string, prepend: string, result: string[]): void { + 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}`); + } + } +} + +export function rreddir(dirPath: string): string[] { + let result: string[] = []; + _rreaddir(dirPath, '', result); + return result; +} + +export function ensureDir(dirPath: string): void { + if (fs.existsSync(dirPath)) { + return; + } + ensureDir(path.dirname(dirPath)); + fs.mkdirSync(dirPath); +} + export function getVersion(root: string): string | undefined { let version = process.env['BUILD_SOURCEVERSION']; @@ -293,3 +320,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/watch-win32.js b/build/lib/watch/watch-win32.js index d0cd307ba1690..91c0eae75659f 100644 --- a/build/lib/watch/watch-win32.js +++ b/build/lib/watch/watch-win32.js @@ -25,7 +25,6 @@ function watch(root) { var child = 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(); @@ -47,7 +46,6 @@ function watch(root) { path: changePathFull, base: root }); - //@ts-ignore file.event = toChangeType(changeType); result.emit('data', file); } @@ -106,4 +104,4 @@ module.exports = function (pattern, options) { }); })) .pipe(rebase); -}; \ No newline at end of file +}; diff --git a/build/lib/watch/yarn.lock b/build/lib/watch/yarn.lock index 3f330da1764b4..edbfe1f3121d8 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/ThirdPartyNotices.txt b/build/monaco/ThirdPartyNotices.txt index 1de70ddaab6c7..8b488daf191a9 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 index a429cd66cde20..1de24d4065c8a 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -4,12 +4,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); +exports.execute = exports.run3 = exports.DeclarationResolver = exports.FSProvider = exports.RECIPE_PATH = void 0; const fs = require("fs"); const ts = require("typescript"); const path = require("path"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); -const dtsv = '2'; +const dtsv = '3'; const tsfmt = require('../../tsfmt.json'); const SRC = path.join(__dirname, '../../src'); exports.RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); @@ -135,11 +136,12 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, } 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};`); } } } @@ -148,12 +150,44 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, } }); } + 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(result); + enums.push({ + enumName: declaration.name.getText(sourceFile), + text: result + }); } return result; } @@ -277,6 +311,14 @@ function format(text, endl) { 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(';'); @@ -292,12 +334,7 @@ function createReplacer(data) { findStr = '\\b' + findStr + '\\b'; directives.push([new RegExp(findStr, 'g'), replaceStr]); }); - return (str) => { - for (let i = 0; i < directives.length; i++) { - str = str.replace(directives[i][0], directives[i][1]); - } - return str; - }; + return createReplacerFromDirectives(directives); } function generateDeclarationFile(recipe, sourceFileGetter) { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; @@ -415,6 +452,15 @@ function generateDeclarationFile(recipe, sourceFileGetter) { resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl); resultTxt = format(resultTxt, endl); resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl); + enums.sort((e1, e2) => { + 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.', @@ -423,7 +469,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) { '', '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', '' - ].concat(enums).join(endl); + ].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); diff --git a/build/monaco/api.ts b/build/monaco/api.ts index ae058292344d7..f3542988a4b02 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -9,7 +9,7 @@ import * as path from 'path'; import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; -const dtsv = '2'; +const dtsv = '3'; const tsfmt = require('../../tsfmt.json'); @@ -138,7 +138,7 @@ function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclarat ); } -function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: string[]): string { +function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: IEnumEntry[]): string { let result = getNodeText(sourceFile, declaration); if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { let interfaceDeclaration = declaration; @@ -167,24 +167,56 @@ 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) { // 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: [RegExp, string][] = []; + 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(result); + enums.push({ + enumName: declaration.name.getText(sourceFile), + text: result + }); } return result; @@ -324,6 +356,15 @@ function format(text: string, endl: string): string { } } +function createReplacerFromDirectives(directives: [RegExp, string][]): (str: string) => string { + return (str: string) => { + for (let i = 0; i < directives.length; i++) { + str = str.replace(directives[i][0], directives[i][1]); + } + return str; + }; +} + function createReplacer(data: string): (str: string) => string { data = data || ''; let rawDirectives = data.split(';'); @@ -341,12 +382,7 @@ function createReplacer(data: string): (str: string) => string { directives.push([new RegExp(findStr, 'g'), replaceStr]); }); - return (str: string) => { - for (let i = 0; i < directives.length; i++) { - str = str.replace(directives[i][0], directives[i][1]); - } - return str; - }; + return createReplacerFromDirectives(directives); } interface ITempResult { @@ -355,6 +391,11 @@ interface ITempResult { enums: string; } +interface IEnumEntry { + enumName: string; + text: string; +} + function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; @@ -376,7 +417,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet return importName; }; - let enums: string[] = []; + let enums: IEnumEntry[] = []; let version: string | null = null; lines.forEach(line => { @@ -492,6 +533,16 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet resultTxt = format(resultTxt, endl); resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl); + enums.sort((e1, e2) => { + 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.', @@ -500,7 +551,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet '', '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', '' - ].concat(enums).join(endl); + ].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); diff --git a/build/monaco/esm.core.js b/build/monaco/esm.core.js new file mode 100644 index 0000000000000..b84b5fb4f4122 --- /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 900b1bcce6064..7288af94c9d99 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 namespace monaco { +declare let MonacoEnvironment: monaco.Environment | undefined; - // THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. +declare namespace monaco { 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; } @@ -43,12 +49,12 @@ declare namespace monaco { } declare namespace monaco.editor { - +#include(vs/editor/browser/widget/diffNavigator): IDiffNavigator #includeAll(vs/editor/standalone/browser/standaloneEditor;modes.=>languages.;editorCommon.=>): #include(vs/editor/standalone/common/standaloneThemeService): BuiltinTheme, IStandaloneThemeData, IColors #include(vs/editor/common/modes/supports/tokenization): ITokenThemeRule #include(vs/editor/common/services/webWorker): MonacoWebWorker, IWebWorkerOptions -#include(vs/editor/standalone/browser/standaloneCodeEditor): IActionDescriptor, IStandaloneEditorConstructionOptions, IDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor +#include(vs/editor/standalone/browser/standaloneCodeEditor): IActionDescriptor, IGlobalEditorOptions, IStandaloneEditorConstructionOptions, IDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor export interface ICommandHandler { (...args: any[]): void; } @@ -88,4 +94,4 @@ declare namespace monaco.worker { } -//dtsv=2 +//dtsv=3 diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index f1a74a25e3013..3c48da8d85a71 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -2,38 +2,17 @@ // This file is adding references to various symbols which should not be removed via tree shaking import { ServiceIdentifier } from './vs/platform/instantiation/common/instantiation'; -import { IContextViewService } from './vs/platform/contextview/browser/contextView'; -import { IHighlight } from './vs/base/parts/quickopen/browser/quickOpenModel'; -import { IWorkspaceContextService } from './vs/platform/workspace/common/workspace'; -import { IEnvironmentService } from './vs/platform/environment/common/environment'; -import { CountBadge } from './vs/base/browser/ui/countBadge/countBadge'; -import { SimpleWorkerClient, create as create1 } from './vs/base/common/worker/simpleWorker'; +import { create as create1 } from './vs/base/common/worker/simpleWorker'; import { create as create2 } from './vs/editor/common/services/editorSimpleWorker'; -import { QuickOpenWidget } from './vs/base/parts/quickopen/browser/quickOpenWidget'; -import { WorkbenchAsyncDataTree } from './vs/platform/list/browser/listService'; import { SyncDescriptor0, SyncDescriptor1, SyncDescriptor2, SyncDescriptor3, SyncDescriptor4, SyncDescriptor5, SyncDescriptor6, SyncDescriptor7, SyncDescriptor8 } from './vs/platform/instantiation/common/descriptors'; -import { DiffNavigator } from './vs/editor/browser/widget/diffNavigator'; -import { DocumentRangeFormattingEditProvider } from './vs/editor/common/modes'; import * as editorAPI from './vs/editor/editor.api'; (function () { var a: any; var b: any; - a = (b).layout; // IContextViewProvider - a = (b).getWorkspaceFolder; // IWorkspaceFolderProvider - a = (b).getWorkspace; // IWorkspaceFolderProvider - a = (b).style; // IThemable - a = (b).style; // IThemable - a = (>b).style; // IThemable - a = (b).userHome; // IUserHomeProvider - a = (b).previous; // IDiffNavigator a = (>b).type; - a = (b).start; - a = (b).end; - a = (>b).getProxyObject; // IWorkerClient a = create1; a = create2; - a = (b).extensionId; // injection madness a = (>b).ctor; diff --git a/build/monaco/monaco.webpack.config.js b/build/monaco/monaco.webpack.config.js new file mode 100644 index 0000000000000..974a341a197e1 --- /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 1ff3e4e72f3fe..70021689eb4a1 100644 --- a/build/monaco/package.json +++ b/build/monaco/package.json @@ -1,7 +1,7 @@ { "name": "monaco-editor-core", "private": true, - "version": "0.18.0", + "version": "0.19.2", "description": "A browser based code editor", "author": "Microsoft Corporation", "license": "MIT", diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index e89344d9139e5..8f8b0019a7792 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,17 +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 -// Remove the windows process tree typings as this causes duplicate identifier errors in tsc builds -const processTreeDts = path.join('node_modules', 'windows-process-tree', 'typings', 'windows-process-tree.d.ts'); -if (fs.existsSync(processTreeDts)) { - console.log('Removing windows-process-tree.d.ts'); - fs.unlinkSync(processTreeDts); -} +cp.execSync('git config pull.rebase true'); diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index eca72654382e0..cb88d37adefd4 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 a685a901b88c2..e185594554b75 100644 --- a/build/package.json +++ b/build/package.json @@ -6,6 +6,7 @@ "@types/ansi-colors": "^3.2.0", "@types/azure": "0.9.19", "@types/debounce": "^1.0.0", + "@types/eslint": "4.16.1", "@types/fancy-log": "^1.3.0", "@types/glob": "^7.1.1", "@types/gulp": "^4.0.5", @@ -28,21 +29,25 @@ "@types/through2": "^2.0.34", "@types/underscore": "^1.8.9", "@types/xml2js": "0.0.33", + "@typescript-eslint/experimental-utils": "~2.13.0", + "@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-bom": "^1.0.0", "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", - "minimist": "^1.2.0", + "minimatch": "3.0.4", + "minimist": "^1.2.3", "request": "^2.85.0", "terser": "4.3.8", - "tslint": "^5.9.1", - "typescript": "3.7.2", + "typescript": "^4.1.0-dev.20200824", "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 0000000000000..d038776c59c64 --- /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 0000000000000..b89250102afca --- /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 df15ccdd1beb4..f075fe3d4f543 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/tslint.json b/build/tslint.json deleted file mode 100644 index 15275279139de..0000000000000 --- a/build/tslint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "rules": { - "no-unused-expression": true, - "no-duplicate-variable": true, - "curly": true, - "class-name": true, - "semicolon": [ - true, - "always" - ], - "triple-equals": true - }, - "defaultSeverity": "warning" -} \ No newline at end of file diff --git a/build/win32/code.iss b/build/win32/code.iss index ee70efb974d02..7f4b36e71aabe 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] @@ -168,6 +173,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.bower 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\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 @@ -182,6 +194,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; 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\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 +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.cjs"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles +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\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 Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.clj"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles @@ -231,6 +250,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.confi 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\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 @@ -423,6 +449,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.hbs"; 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\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 @@ -957,16 +990,16 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Applications\{#ExeBas 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\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1""" -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open w&ith {#ShellNameShort}"; Tasks: addcontextmenufiles; Flags: uninsdeletekey +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufiles; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\*\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\*\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: addcontextmenufiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open w&ith {#ShellNameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open w&ith {#ShellNameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\background\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\directory\background\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open w&ith {#ShellNameShort}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: ""; ValueData: "{cm:OpenWithCodeContextMenu,{#ShellNameShort}}"; Tasks: addcontextmenufolders; Flags: uninsdeletekey Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\{#ExeBasename}.exe"; Tasks: addcontextmenufolders Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\Drive\shell\{#RegValueName}\command"; ValueType: expandsz; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%V"""; Tasks: addcontextmenufolders @@ -996,7 +1029,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; @@ -1004,7 +1037,7 @@ begin #endif #if "user" == InstallTarget - #if "ia32" == Arch + #if "ia32" == Arch || "arm64" == Arch #define IncompatibleArchRootKey "HKLM32" #else #define IncompatibleArchRootKey "HKLM64" @@ -1114,7 +1147,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 0000000000000..8c57d20a59aef --- /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 370da6b37c712..0000000000000 --- 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 a7c38d12b9f90..0f1b1a7ccf521 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 e384e83d30d36..5c5df9a166f0d 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 4746349e191bd..fb748761f99e2 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.de.isl b/build/win32/i18n/messages.de.isl index 755652bcdbe95..6a9f29aa9c27e 100644 --- a/build/win32/i18n/messages.de.isl +++ b/build/win32/i18n/messages.de.isl @@ -5,4 +5,5 @@ AssociateWithFiles=%1 als Editor f AddToPath=Zu PATH hinzufgen (nach dem Neustart verfgbar) RunAfter=%1 nach der Installation ausfhren Other=Andere: -SourceFile=%1-Quelldatei \ No newline at end of file +SourceFile=%1-Quelldatei +OpenWithCodeContextMenu=Mit %1 ffnen \ No newline at end of file diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl index a6aab59b95a97..986eba00d3e58 100644 --- a/build/win32/i18n/messages.en.isl +++ b/build/win32/i18n/messages.en.isl @@ -1,8 +1,16 @@ +[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 AddToPath=Add to PATH (requires shell restart) RunAfter=Run %1 after installation Other=Other: -SourceFile=%1 Source File \ No newline at end of file +SourceFile=%1 Source File +OpenWithCodeContextMenu=Open w&ith %1 diff --git a/build/win32/i18n/messages.es.isl b/build/win32/i18n/messages.es.isl index e8d5af64b6193..e51f099f9a136 100644 --- a/build/win32/i18n/messages.es.isl +++ b/build/win32/i18n/messages.es.isl @@ -5,4 +5,5 @@ AssociateWithFiles=Registrar %1 como editor para tipos de archivo admitidos AddToPath=Agregar a PATH (disponible despus de reiniciar) RunAfter=Ejecutar %1 despus de la instalacin Other=Otros: -SourceFile=Archivo de origen %1 \ No newline at end of file +SourceFile=Archivo de origen %1 +OpenWithCodeContextMenu=Abrir con %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.fr.isl b/build/win32/i18n/messages.fr.isl index 3e17036f80673..df14041862513 100644 --- a/build/win32/i18n/messages.fr.isl +++ b/build/win32/i18n/messages.fr.isl @@ -5,4 +5,5 @@ AssociateWithFiles=Inscrire %1 en tant qu' AddToPath=Ajouter PATH (disponible aprs le redmarrage) RunAfter=Excuter %1 aprs l'installation Other=Autre: -SourceFile=Fichier source %1 \ No newline at end of file +SourceFile=Fichier source %1 +OpenWithCodeContextMenu=Ouvrir avec %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.hu.isl b/build/win32/i18n/messages.hu.isl index f141ad1b24ad6..b64553da8e6de 100644 --- a/build/win32/i18n/messages.hu.isl +++ b/build/win32/i18n/messages.hu.isl @@ -5,4 +5,5 @@ AssociateWithFiles=%1 regisztr AddToPath=Hozzads a PATH-hoz (jraindts utn lesz elrhet) RunAfter=%1 indtsa a telepts utn Other=Egyb: -SourceFile=%1 forrsfjl \ No newline at end of file +SourceFile=%1 forrsfjl +OpenWithCodeContextMenu=Megnyits a kvetkezvel: %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.it.isl b/build/win32/i18n/messages.it.isl index 7ad8a0622d45a..08248c4ce1ba8 100644 --- a/build/win32/i18n/messages.it.isl +++ b/build/win32/i18n/messages.it.isl @@ -5,4 +5,5 @@ AssociateWithFiles=Registra %1 come editor per i tipi di file supportati AddToPath=Aggiungi a PATH (disponibile dopo il riavvio) RunAfter=Esegui %1 dopo l'installazione Other=Altro: -SourceFile=File di origine %1 \ No newline at end of file +SourceFile=File di origine %1 +OpenWithCodeContextMenu=Apri con %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.ja.isl b/build/win32/i18n/messages.ja.isl index 3a16aaa204e97..9675060e94afd 100644 --- a/build/win32/i18n/messages.ja.isl +++ b/build/win32/i18n/messages.ja.isl @@ -5,4 +5,5 @@ AssociateWithFiles= AddToPath=PATH ւ̒ljiċNɎgp”\j RunAfter=CXg[ %1 s Other=̑: -SourceFile=%1 \[X t@C \ No newline at end of file +SourceFile=%1 \[X t@C +OpenWithCodeContextMenu=%1 ŊJ \ No newline at end of file diff --git a/build/win32/i18n/messages.ko.isl b/build/win32/i18n/messages.ko.isl index 28860c36b633e..5a510558bbd2b 100644 --- a/build/win32/i18n/messages.ko.isl +++ b/build/win32/i18n/messages.ko.isl @@ -5,4 +5,5 @@ AssociateWithFiles=%1 AddToPath=PATH ߰(ٽ ) RunAfter=ġ %1 Other=Ÿ: -SourceFile=%1 \ No newline at end of file +SourceFile=%1 +OpenWithCodeContextMenu=%1() \ No newline at end of file diff --git a/build/win32/i18n/messages.pt-br.isl b/build/win32/i18n/messages.pt-br.isl index d534637f8b613..e327e8fd1a066 100644 --- a/build/win32/i18n/messages.pt-br.isl +++ b/build/win32/i18n/messages.pt-br.isl @@ -5,4 +5,5 @@ AssociateWithFiles=Registre %1 como um editor para tipos de arquivos suportados AddToPath=Adicione em PATH (disponvel aps reiniciar) RunAfter=Executar %1 aps a instalao Other=Outros: -SourceFile=Arquivo Fonte %1 \ No newline at end of file +SourceFile=Arquivo Fonte %1 +OpenWithCodeContextMenu=Abrir com %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.ru.isl b/build/win32/i18n/messages.ru.isl index 4d834663627e1..bca3b864a5f9e 100644 --- a/build/win32/i18n/messages.ru.isl +++ b/build/win32/i18n/messages.ru.isl @@ -5,4 +5,5 @@ AssociateWithFiles= AddToPath= PATH ( ) RunAfter= %1 Other=: -SourceFile= %1 \ No newline at end of file +SourceFile= %1 +OpenWithCodeContextMenu= %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.tr.isl b/build/win32/i18n/messages.tr.isl index dc241d924c7bc..b13e5e27bd2af 100644 --- a/build/win32/i18n/messages.tr.isl +++ b/build/win32/i18n/messages.tr.isl @@ -5,4 +5,5 @@ AssociateWithFiles=%1 uygulamas AddToPath=PATH'e ekle (yeniden balattktan sonra kullanlabilir) RunAfter=Kurulumdan sonra %1 uygulamasn altr. Other=Dier: -SourceFile=%1 Kaynak Dosyas \ No newline at end of file +SourceFile=%1 Kaynak Dosyas +OpenWithCodeContextMenu=%1 le A \ No newline at end of file diff --git a/build/win32/i18n/messages.zh-cn.isl b/build/win32/i18n/messages.zh-cn.isl index 349fc2ccc29b5..8fa136f6d5a9b 100644 --- a/build/win32/i18n/messages.zh-cn.isl +++ b/build/win32/i18n/messages.zh-cn.isl @@ -5,4 +5,5 @@ AssociateWithFiles= AddToPath=ӵ PATH (Ч) RunAfter=װ %1 Other=: -SourceFile=%1 Դļ \ No newline at end of file +SourceFile=%1 Դļ +OpenWithCodeContextMenu=ͨ %1 \ No newline at end of file diff --git a/build/win32/i18n/messages.zh-tw.isl b/build/win32/i18n/messages.zh-tw.isl index 7c3f84aa131f5..40c5fa92d793e 100644 --- a/build/win32/i18n/messages.zh-tw.isl +++ b/build/win32/i18n/messages.zh-tw.isl @@ -5,4 +5,5 @@ AssociateWithFiles= AddToPath=[J PATH (sҰʫͮ) RunAfter=w˫ %1 Other=L: -SourceFile=%1 ӷɮ \ No newline at end of file +SourceFile=%1 ӷɮ +OpenWithCodeContextMenu=H %1 } \ No newline at end of file diff --git a/build/yarn.lock b/build/yarn.lock index 36f6c025d487c..01ebd186c4cbc 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -90,6 +90,24 @@ resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + +"@types/eslint@4.16.1": + version "4.16.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-4.16.1.tgz#19730c9fcb66b6e44742d12b27a603fabfeb2f49" + integrity sha512-lRUXQAULl5geixTiP2K0iYvMUbCkEnuOwvLGjwff12I4ECxoW5QaWML5UUOZ1CvpQLILkddBdMPMZz4ByQizsg== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "0.0.41" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.41.tgz#fd90754150b57432b72bf560530500597ff04421" + integrity sha512-rIAmXyJlqw4KEBO7+u9gxZZSQHaCNnIzYrnNmYVpgfJhxTqO0brCX0SYpqUTkVI5mwwUwzmtspLBGBKroMeynA== + "@types/events@*": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" @@ -184,6 +202,11 @@ resolved "https://registry.yarnpkg.com/@types/js-beautify/-/js-beautify-1.8.0.tgz#0369d3d0e1f35a6aec07cb4da2ee2bcda111367c" integrity sha512-/siF86XrwDKLuHe8l7h6NhrAWgLdgqbxmjZv9NvGWmgYRZoTipkjKiWb0SQHy/jcR+ee0GvbG6uGd+LEBMGNvA== +"@types/json-schema@*", "@types/json-schema@^7.0.3": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + "@types/mime@0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b" @@ -312,11 +335,70 @@ resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.0.33.tgz#20c5dd6460245284d64a55690015b95e409fb7de" integrity sha1-IMXdZGAkUoTWSlVpABW5XkCft94= +"@typescript-eslint/experimental-utils@2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.14.0.tgz#e9179fa3c44e00b3106b85d7b69342901fb43e3b" + integrity sha512-KcyKS7G6IWnIgl3ZpyxyBCxhkBPV+0a5Jjy2g5HxlrbG2ZLQNFeneIBVXdaBCYOVjvGmGGFKom1kgiAY75SDeQ== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.14.0" + eslint-scope "^5.0.0" + +"@typescript-eslint/experimental-utils@~2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.13.0.tgz#958614faa6f77599ee2b241740e0ea402482533d" + integrity sha512-+Hss3clwa6aNiC8ZjA45wEm4FutDV5HsVXPl/rDug1THq6gEtOYRGLqS3JlTk7mSnL5TbJz0LpEbzbPnKvY6sw== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.13.0" + eslint-scope "^5.0.0" + +"@typescript-eslint/parser@^2.12.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.14.0.tgz#30fa0523d86d74172a5e32274558404ba4262cd6" + integrity sha512-haS+8D35fUydIs+zdSf4BxpOartb/DjrZ2IxQ5sR8zyGfd77uT9ZJZYF8+I0WPhzqHmfafUBx8MYpcp8pfaoSA== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.14.0" + "@typescript-eslint/typescript-estree" "2.14.0" + eslint-visitor-keys "^1.1.0" + +"@typescript-eslint/typescript-estree@2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.13.0.tgz#a2e746867da772c857c13853219fced10d2566bc" + integrity sha512-t21Mg5cc8T3ADEUGwDisHLIubgXKjuNRbkpzDMLb7/JMmgCe/gHM9FaaujokLey+gwTuLF5ndSQ7/EfQqrQx4g== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash.unescape "4.0.1" + semver "^6.3.0" + tsutils "^3.17.1" + +"@typescript-eslint/typescript-estree@2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.14.0.tgz#c67698acdc14547f095eeefe908958d93e1a648d" + integrity sha512-pnLpUcMNG7GfFFfNQbEX6f1aPa5fMnH2G9By+A1yovYI4VIOK2DzkaRuUlIkbagpAcrxQHLqovI1YWqEcXyRnA== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash.unescape "4.0.1" + semver "^6.3.0" + tsutils "^3.17.1" + acorn@4.X: version "4.0.13" 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" @@ -352,13 +434,6 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -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== - dependencies: - color-convert "^1.9.0" - ansi-wrap@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" @@ -482,20 +557,16 @@ azure-storage@^2.1.0: xml2js "0.2.7" xmlbuilder "0.4.3" -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.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" 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" @@ -508,6 +579,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" @@ -554,27 +630,40 @@ 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-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== -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= - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.0.0, chalk@^1.1.3: +chalk@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -585,15 +674,6 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.3.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - cheerio@^1.0.0-rc.1: version "1.0.0-rc.2" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" @@ -626,18 +706,6 @@ 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@^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== - 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= - color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" @@ -672,16 +740,21 @@ command-line-args@^5.1.1: lodash.camelcase "^4.3.0" typical "^4.0.0" -commander@^2.12.1, commander@^2.8.1: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== - commander@^2.20.0, commander@~2.20.0: version "2.20.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@^2.8.1: + version "2.19.0" + 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" @@ -770,14 +843,14 @@ 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== @@ -816,11 +889,6 @@ diagnostic-channel@0.2.0: dependencies: semver "^5.3.0" -diff@^3.2.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -888,6 +956,18 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.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" + 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" @@ -900,20 +980,35 @@ 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== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +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" 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== +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" -esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= +eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.1.0, estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== execa@^1.0.0: version "1.0.0" @@ -1078,7 +1173,7 @@ glob-parent@^5.0.0: dependencies: is-glob "^4.0.1" -glob@^7.0.6, glob@^7.1.1: +glob@^7.0.6: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== @@ -1102,6 +1197,18 @@ 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== + 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" + globby@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22" @@ -1238,11 +1345,6 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^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= - has-gulplog@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" @@ -1318,12 +1420,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" @@ -1436,6 +1544,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" @@ -1451,19 +1566,6 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - -js-yaml@^3.7.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" - integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -1498,6 +1600,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" @@ -1641,10 +1748,15 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" +lodash.unescape@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + 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" @@ -1736,10 +1848,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" @@ -1900,11 +2012,6 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -1930,6 +2037,15 @@ 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" + prettyjson@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289" @@ -1953,6 +2069,11 @@ 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== +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== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -2103,13 +2224,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.3.2: - version "1.8.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" - integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== - dependencies: - path-parse "^1.0.5" - reusify@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -2135,11 +2249,6 @@ 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" @@ -2165,6 +2274,11 @@ semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -2301,13 +2415,6 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= -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" - terser@*: version "4.2.1" resolved "https://registry.yarnpkg.com/terser/-/terser-4.2.1.tgz#1052cfe17576c66e7bc70fcc7119f22b155bdac1" @@ -2381,7 +2488,7 @@ ts-morph@^3.1.3: multimatch "^4.0.0" typescript "^3.0.1" -tslib@^1.8.0, tslib@^1.8.1: +tslib@^1.8.1: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== @@ -2391,28 +2498,10 @@ tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tslint@^5.9.1: - version "5.11.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" - integrity sha1-mPMMAurjzecAYgHkwzywi0hYHu0= - dependencies: - babel-code-frame "^6.22.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^3.2.0" - glob "^7.1.1" - js-yaml "^3.7.0" - minimatch "^3.0.4" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.8.0" - tsutils "^2.27.2" - -tsutils@^2.27.2: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== dependencies: tslib "^1.8.1" @@ -2441,16 +2530,16 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" -typescript@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== - 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.20200824: + version "4.1.0-dev.20200824" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.0-dev.20200824.tgz#34c92d9b6e5124600658c0d4e9b8c125beaf577d" + integrity sha512-hTJfocmebnMKoqRw/xs3bL61z87XXtvOUwYtM7zaCX9mAvnfdo1x1bzQlLZAsvdzRIgAHPJQYbqYHKygWkDw6g== + typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" @@ -2581,19 +2670,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" @@ -2649,11 +2741,21 @@ 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.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" diff --git a/cglicenses.json b/cglicenses.json index 1e9287cbaf28b..0da22bd9f5728 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -81,5 +81,283 @@ "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", + "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." + ] } ] diff --git a/cgmanifest.json b/cgmanifest.json index 53dab5356a231..859af568b1bb2 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "91f08db83c2ce8c722ddf0911ead8f7c473bedfa" + "commitHash": "894fb9eb56c6cbda65e3c3ae9ada6d4cb5850cc9" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "76.0.3809.146" + "version": "83.0.4103.122" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "64219741218aa87e259cf8257596073b8e747f0a" + "commitHash": "9622fed3fb2cffcea9efff6c8cb4cc2def99d75d" } }, "isOnlyProductionDependency": true, - "version": "12.4.0" + "version": "12.14.1" }, { "component": { @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "dabaa7557a165cc107f89fd7c3e3c3210f9b5758" + "commitHash": "fb03807cd21915ddc3aa2521ba4f5ba14597bd7e" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "6.1.2" + "version": "9.3.0" }, { "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": "1f8534396cda86d3fa199b0e45ce110b55d794d0" + "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 2fb5445a34a47..e205fb5f5c4eb 100644 --- a/extensions/bat/language-configuration.json +++ b/extensions/bat/language-configuration.json @@ -1,6 +1,6 @@ { "comments": { - "lineComment": "REM" + "lineComment": "@REM" }, "brackets": [ ["{", "}"], @@ -11,18 +11,19 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + { "open": "\"", "close": "\"", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], + ["%", "%"], ["\"", "\""] ], "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 00bd84e4ae42f..dc543aef40272 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 97155fafc8b8b..eb76b0e1d0419 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/clojure/language-configuration.json b/extensions/clojure/language-configuration.json index 24f5248001560..a62cb968ece20 100644 --- a/extensions/clojure/language-configuration.json +++ b/extensions/clojure/language-configuration.json @@ -11,7 +11,7 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + { "open": "\"", "close": "\"", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -22,4 +22,4 @@ "folding": { "offSide": true } -} \ No newline at end of file +} diff --git a/extensions/clojure/test/colorize-results/test_clj.json b/extensions/clojure/test/colorize-results/test_clj.json index b7dd17d91e886..64b91983a1017 100644 --- a/extensions/clojure/test/colorize-results/test_clj.json +++ b/extensions/clojure/test/colorize-results/test_clj.json @@ -202,9 +202,9 @@ "t": "source.clojure meta.expression.clojure meta.definition.global.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -235,9 +235,9 @@ "t": "source.clojure meta.vector.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -257,9 +257,9 @@ "t": "source.clojure meta.vector.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -279,9 +279,9 @@ "t": "source.clojure meta.vector.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -345,9 +345,9 @@ "t": "source.clojure meta.vector.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -466,9 +466,9 @@ "t": "source.clojure meta.map.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -510,9 +510,9 @@ "t": "source.clojure meta.map.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -620,9 +620,9 @@ "t": "source.clojure meta.quoted-expression.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -642,9 +642,9 @@ "t": "source.clojure meta.quoted-expression.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -664,9 +664,9 @@ "t": "source.clojure meta.quoted-expression.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1368,9 +1368,9 @@ "t": "source.clojure meta.quoted-expression.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1390,9 +1390,9 @@ "t": "source.clojure meta.quoted-expression.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1412,9 +1412,9 @@ "t": "source.clojure meta.quoted-expression.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1522,9 +1522,9 @@ "t": "source.clojure meta.expression.clojure meta.vector.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1566,9 +1566,9 @@ "t": "source.clojure meta.expression.clojure meta.vector.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1610,9 +1610,9 @@ "t": "source.clojure meta.expression.clojure meta.vector.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2567,9 +2567,9 @@ "t": "source.clojure meta.expression.clojure meta.definition.global.clojure meta.map.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2611,9 +2611,9 @@ "t": "source.clojure meta.expression.clojure meta.definition.global.clojure meta.map.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2721,9 +2721,9 @@ "t": "source.clojure meta.expression.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3007,9 +3007,9 @@ "t": "source.clojure meta.expression.clojure meta.definition.global.clojure meta.expression.clojure meta.map.clojure constant.numeric.long.clojure", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3321,4 +3321,4 @@ "hc_black": "comment: #7CA668" } } -] \ No newline at end of file +] diff --git a/extensions/coffeescript/language-configuration.json b/extensions/coffeescript/language-configuration.json index fd4a1b88c87dd..c2b8662039d61 100644 --- a/extensions/coffeescript/language-configuration.json +++ b/extensions/coffeescript/language-configuration.json @@ -12,8 +12,8 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], diff --git a/extensions/coffeescript/package.json b/extensions/coffeescript/package.json index df5f8ff7c1f78..9bc96cca5d83b 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-regex_coffee.json b/extensions/coffeescript/test/colorize-results/test-regex_coffee.json index 9daab0d553371..15f4b3cd38449 100644 --- a/extensions/coffeescript/test/colorize-results/test-regex_coffee.json +++ b/extensions/coffeescript/test/colorize-results/test-regex_coffee.json @@ -147,9 +147,9 @@ "t": "source.coffee constant.numeric.decimal.coffee", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -191,9 +191,9 @@ "t": "source.coffee constant.numeric.decimal.coffee", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -202,9 +202,9 @@ "t": "source.coffee constant.numeric.decimal.coffee", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -224,9 +224,9 @@ "t": "source.coffee constant.numeric.decimal.coffee", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -725,4 +725,4 @@ "hc_black": "string.regexp: #D16969" } } -] \ No newline at end of file +] diff --git a/extensions/coffeescript/test/colorize-results/test_coffee.json b/extensions/coffeescript/test/colorize-results/test_coffee.json index d3de07d3f82de..9647307f073bd 100644 --- a/extensions/coffeescript/test/colorize-results/test_coffee.json +++ b/extensions/coffeescript/test/colorize-results/test_coffee.json @@ -1016,9 +1016,9 @@ "t": "source.coffee constant.numeric.decimal.coffee", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1038,9 +1038,9 @@ "t": "source.coffee constant.numeric.decimal.coffee", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -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" diff --git a/extensions/configuration-editing/.vscodeignore b/extensions/configuration-editing/.vscodeignore index f12c396fd04d5..de8e6dc591301 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/extensions/configuration-editing/extension-browser.webpack.config.js b/extensions/configuration-editing/extension-browser.webpack.config.js new file mode 100644 index 0000000000000..de9416253737d --- /dev/null +++ b/extensions/configuration-editing/extension-browser.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 withBrowserDefaults = require('../shared.webpack.config').browser; + +module.exports = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/configurationEditingMain.ts' + }, + output: { + filename: 'configurationEditingMain.js' + } +}); + diff --git a/extensions/configuration-editing/extension.webpack.config.js b/extensions/configuration-editing/extension.webpack.config.js index b474e65cbb130..1b18dd86a9911 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 5defc86cf5d9e..3afe9fa4332ab 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" }, { @@ -88,10 +89,18 @@ "fileMatch": "/.vscode/tasks.json", "url": "vscode://schemas/tasks" }, + { + "fileMatch": "%APP_SETTINGS_HOME%/tasks.json", + "url": "vscode://schemas/tasks" + }, { "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" @@ -108,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 5f3d28abb89ec..2b7952446f0d2 100644 --- a/extensions/configuration-editing/schemas/attachContainer.schema.json +++ b/extensions/configuration-editing/schemas/attachContainer.schema.json @@ -1,7 +1,8 @@ { - "$schema": "http://json-schema.org/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "description": "Configures an attached to container", "allowComments": true, + "allowTrailingCommas": true, "type": "object", "definitions": { "attachContainer": { @@ -18,9 +19,49 @@ "type": "integer" } }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container." + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables." + }, + "remoteUser": { + "type": "string", + "description": "The user VS Code Server will be started with. The default is the same user as the container." + }, "extensions": { "type": "array", "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]*)(@(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 1358c74f2d2a9..b37e07fa65ceb 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -1,7 +1,8 @@ { - "$schema": "http://json-schema.org/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "description": "Defines a dev container", "allowComments": true, + "allowTrailingCommas": true, "type": "object", "definitions": { "devContainerCommon": { @@ -15,13 +16,48 @@ "type": "array", "description": "An array of extensions that should be installed into the container.", "items": { - "type": "string" + "type": "string", + "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." }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer", + "maximum": 65535, + "minimum": 0 + } + }, + "remoteEnv": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "description": "Remote environment variables." + }, + "remoteUser": { + "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", @@ -32,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." } } }, @@ -55,6 +125,28 @@ ] } }, + "containerEnv": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Container environment variables." + }, + "containerUser": { + "type": "string", + "description": "The user the container will be started with. The default is the user on the Docker image." + }, + "updateRemoteUserUID": { + "type": "boolean", + "description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default." + }, + "mounts": { + "type": "array", + "description": "Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax.", + "items": { + "type": "string" + } + }, "runArgs": { "type": "array", "description": "The arguments required when starting in the container.", @@ -84,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", @@ -156,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/configurationEditingMain.ts b/extensions/configuration-editing/src/configurationEditingMain.ts new file mode 100644 index 0000000000000..966073e23f87b --- /dev/null +++ b/extensions/configuration-editing/src/configurationEditingMain.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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 { + //settings.json suggestions + context.subscriptions.push(registerSettingsCompletions()); + + //extensions suggestions + context.subscriptions.push(...registerExtensionsCompletions()); + + // launch.json variable suggestions + context.subscriptions.push(registerVariableCompletions('**/launch.json')); + + // task.json variable suggestions + context.subscriptions.push(registerVariableCompletions('**/tasks.json')); +} + +function registerSettingsCompletions(): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider({ language: 'jsonc', pattern: '**/settings.json' }, { + provideCompletionItems(document, position, token) { + return new SettingsDocument(document).provideCompletionItems(position, token); + } + }); +} + +function registerVariableCompletions(pattern: string): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider({ language: 'jsonc', pattern }, { + provideCompletionItems(document, position, _token) { + const location = getLocation(document.getText(), document.offsetAt(position)); + if (!location.isAtPropertyKey && location.previousNode && location.previousNode.type === 'string') { + const indexOf$ = document.lineAt(position.line).text.indexOf('$'); + const startPosition = indexOf$ >= 0 ? new vscode.Position(position.line, indexOf$) : position; + + return [ + { label: 'workspaceFolder', detail: localize('workspaceFolder', "The path of the folder opened in VS Code") }, + { label: 'workspaceFolderBasename', detail: localize('workspaceFolderBasename', "The name of the folder opened in VS Code without any slashes (/)") }, + { label: 'relativeFile', detail: localize('relativeFile', "The current opened file relative to ${workspaceFolder}") }, + { label: 'relativeFileDirname', detail: localize('relativeFileDirname', "The current opened file's dirname relative to ${workspaceFolder}") }, + { label: 'file', detail: localize('file', "The current opened file") }, + { label: 'cwd', detail: localize('cwd', "The task runner's current working directory on startup") }, + { label: 'lineNumber', detail: localize('lineNumber', "The current selected line number in the active file") }, + { label: 'selectedText', detail: localize('selectedText', "The current selected text in the active file") }, + { label: 'fileDirname', detail: localize('fileDirname', "The current opened file's dirname") }, + { label: 'fileExtname', detail: localize('fileExtname', "The current opened file's extension") }, + { label: 'fileBasename', detail: localize('fileBasename', "The current opened file's basename") }, + { label: 'fileBasenameNoExtension', detail: localize('fileBasenameNoExtension', "The current opened file's basename with no file extension") }, + { label: 'defaultBuildTask', detail: localize('defaultBuildTask', "The name of the default build task. If there is not a single default build task then a quick pick is shown to choose the build task.") }, + ].map(variable => ({ + label: '${' + variable.label + '}', + range: new vscode.Range(startPosition, position), + detail: variable.detail + })); + } + + return []; + } + }); +} + +interface IExtensionsContent { + recommendations: string[]; +} + +function registerExtensionsCompletions(): vscode.Disposable[] { + return [registerExtensionsCompletionsInExtensionsDocument(), registerExtensionsCompletionsInWorkspaceConfigurationDocument()]; +} + +function registerExtensionsCompletionsInExtensionsDocument(): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider({ pattern: '**/extensions.json' }, { + provideCompletionItems(document, position, _token) { + const location = getLocation(document.getText(), document.offsetAt(position)); + const range = document.getWordRangeAtPosition(position) || new vscode.Range(position, position); + if (location.path[0] === 'recommendations') { + const extensionsContent = parse(document.getText()); + return provideInstalledExtensionProposals(extensionsContent && extensionsContent.recommendations || [], range, false); + } + return []; + } + }); +} + +function registerExtensionsCompletionsInWorkspaceConfigurationDocument(): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider({ pattern: '**/*.code-workspace' }, { + provideCompletionItems(document, position, _token) { + const location = getLocation(document.getText(), document.offsetAt(position)); + 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 && extensionsContent.recommendations || [], range, false); + } + return []; + } + }); +} + +vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'jsonc' }, { + provideDocumentSymbols(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult { + const result: vscode.SymbolInformation[] = []; + let name: string = ''; + let lastProperty = ''; + let startOffset = 0; + let depthInObjects = 0; + + visit(document.getText(), { + onObjectProperty: (property, _offset, _length) => { + lastProperty = property; + }, + onLiteralValue: (value: any, _offset: number, _length: number) => { + if (lastProperty === 'name') { + name = value; + } + }, + onObjectBegin: (offset: number, _length: number) => { + depthInObjects++; + if (depthInObjects === 2) { + startOffset = offset; + } + }, + onObjectEnd: (offset: number, _length: number) => { + if (name && depthInObjects === 2) { + result.push(new vscode.SymbolInformation(name, vscode.SymbolKind.Object, new vscode.Range(document.positionAt(startOffset), document.positionAt(offset)))); + } + depthInObjects--; + }, + }); + + return result; + } +}, { label: 'Launch Targets' }); diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/extension.ts deleted file mode 100644 index 72e2fcf627f94..0000000000000 --- a/extensions/configuration-editing/src/extension.ts +++ /dev/null @@ -1,222 +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 { getLocation, parse, visit } from 'jsonc-parser'; -import * as path from 'path'; -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import { SettingsDocument } from './settingsDocumentHelper'; -const localize = nls.loadMessageBundle(); - -const fadedDecoration = vscode.window.createTextEditorDecorationType({ - light: { - color: '#757575' - }, - dark: { - color: '#878787' - } -}); - -let pendingLaunchJsonDecoration: NodeJS.Timer; - -export function activate(context: vscode.ExtensionContext): void { - //settings.json suggestions - context.subscriptions.push(registerSettingsCompletions()); - - //extensions suggestions - context.subscriptions.push(...registerExtensionsCompletions()); - - // launch.json variable suggestions - context.subscriptions.push(registerVariableCompletions('**/launch.json')); - - // task.json variable suggestions - context.subscriptions.push(registerVariableCompletions('**/tasks.json')); - - // launch.json decorations - context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(editor => updateLaunchJsonDecorations(editor), null, context.subscriptions)); - context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(event => { - if (vscode.window.activeTextEditor && event.document === vscode.window.activeTextEditor.document) { - if (pendingLaunchJsonDecoration) { - clearTimeout(pendingLaunchJsonDecoration); - } - pendingLaunchJsonDecoration = setTimeout(() => updateLaunchJsonDecorations(vscode.window.activeTextEditor), 1000); - } - }, null, context.subscriptions)); - updateLaunchJsonDecorations(vscode.window.activeTextEditor); -} - -function registerSettingsCompletions(): vscode.Disposable { - return vscode.languages.registerCompletionItemProvider({ language: 'jsonc', pattern: '**/settings.json' }, { - provideCompletionItems(document, position, token) { - return new SettingsDocument(document).provideCompletionItems(position, token); - } - }); -} - -function registerVariableCompletions(pattern: string): vscode.Disposable { - return vscode.languages.registerCompletionItemProvider({ language: 'jsonc', pattern }, { - provideCompletionItems(document, position, _token) { - const location = getLocation(document.getText(), document.offsetAt(position)); - if (!location.isAtPropertyKey && location.previousNode && location.previousNode.type === 'string') { - const indexOf$ = document.lineAt(position.line).text.indexOf('$'); - const startPosition = indexOf$ >= 0 ? new vscode.Position(position.line, indexOf$) : position; - - return [ - { label: 'workspaceFolder', detail: localize('workspaceFolder', "The path of the folder opened in VS Code") }, - { label: 'workspaceFolderBasename', detail: localize('workspaceFolderBasename', "The name of the folder opened in VS Code without any slashes (/)") }, - { label: 'relativeFile', detail: localize('relativeFile', "The current opened file relative to ${workspaceFolder}") }, - { label: 'relativeFileDirname', detail: localize('relativeFileDirname', "The current opened file's dirname relative to ${workspaceFolder}") }, - { label: 'file', detail: localize('file', "The current opened file") }, - { label: 'cwd', detail: localize('cwd', "The task runner's current working directory on startup") }, - { label: 'lineNumber', detail: localize('lineNumber', "The current selected line number in the active file") }, - { label: 'selectedText', detail: localize('selectedText', "The current selected text in the active file") }, - { label: 'fileDirname', detail: localize('fileDirname', "The current opened file's dirname") }, - { label: 'fileExtname', detail: localize('fileExtname', "The current opened file's extension") }, - { label: 'fileBasename', detail: localize('fileBasename', "The current opened file's basename") }, - { label: 'fileBasenameNoExtension', detail: localize('fileBasenameNoExtension', "The current opened file's basename with no file extension") }, - { label: 'defaultBuildTask', detail: localize('defaultBuildTask', "The name of the default build task. If there is not a single default build task then a quick pick is shown to choose the build task.") }, - ].map(variable => ({ - label: '${' + variable.label + '}', - range: new vscode.Range(startPosition, position), - detail: variable.detail - })); - } - - return []; - } - }); -} - -interface IExtensionsContent { - recommendations: string[]; -} - -function registerExtensionsCompletions(): vscode.Disposable[] { - return [registerExtensionsCompletionsInExtensionsDocument(), registerExtensionsCompletionsInWorkspaceConfigurationDocument()]; -} - -function registerExtensionsCompletionsInExtensionsDocument(): vscode.Disposable { - return vscode.languages.registerCompletionItemProvider({ pattern: '**/extensions.json' }, { - provideCompletionItems(document, position, _token) { - const location = getLocation(document.getText(), document.offsetAt(position)); - 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 []; - } - }); -} - -function registerExtensionsCompletionsInWorkspaceConfigurationDocument(): vscode.Disposable { - return vscode.languages.registerCompletionItemProvider({ pattern: '**/*.code-workspace' }, { - provideCompletionItems(document, position, _token) { - const location = getLocation(document.getText(), document.offsetAt(position)); - 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 []; - } - }); -} - -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; -} - -function updateLaunchJsonDecorations(editor: vscode.TextEditor | undefined): void { - if (!editor || path.basename(editor.document.fileName) !== 'launch.json') { - return; - } - - const ranges: vscode.Range[] = []; - let addPropertyAndValue = false; - let depthInArray = 0; - visit(editor.document.getText(), { - onObjectProperty: (property, offset, length) => { - // Decorate attributes which are unlikely to be edited by the user. - // Only decorate "configurations" if it is not inside an array (compounds have a configurations property which should not be decorated). - addPropertyAndValue = property === 'version' || property === 'type' || property === 'request' || property === 'compounds' || (property === 'configurations' && depthInArray === 0); - if (addPropertyAndValue) { - ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length))); - } - }, - onLiteralValue: (_value, offset, length) => { - if (addPropertyAndValue) { - ranges.push(new vscode.Range(editor.document.positionAt(offset), editor.document.positionAt(offset + length))); - } - }, - onArrayBegin: (_offset: number, _length: number) => { - depthInArray++; - }, - onArrayEnd: (_offset: number, _length: number) => { - depthInArray--; - } - }); - - editor.setDecorations(fadedDecoration, ranges); -} - -vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', language: 'jsonc' }, { - provideDocumentSymbols(document: vscode.TextDocument, _token: vscode.CancellationToken): vscode.ProviderResult { - const result: vscode.SymbolInformation[] = []; - let name: string = ''; - let lastProperty = ''; - let startOffset = 0; - let depthInObjects = 0; - - visit(document.getText(), { - onObjectProperty: (property, _offset, _length) => { - lastProperty = property; - }, - onLiteralValue: (value: any, _offset: number, _length: number) => { - if (lastProperty === 'name') { - name = value; - } - }, - onObjectBegin: (offset: number, _length: number) => { - depthInObjects++; - if (depthInObjects === 2) { - startOffset = offset; - } - }, - onObjectEnd: (offset: number, _length: number) => { - if (name && depthInObjects === 2) { - result.push(new vscode.SymbolInformation(name, vscode.SymbolKind.Object, new vscode.Range(document.positionAt(startOffset), document.positionAt(offset)))); - } - depthInObjects--; - }, - }); - - return result; - } -}, { label: 'Launch Targets' }); diff --git a/extensions/configuration-editing/src/extensionsProposals.ts b/extensions/configuration-editing/src/extensionsProposals.ts new file mode 100644 index 0000000000000..60533ae29759e --- /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 970f2a5ca18c3..5e466c2eb6fa7 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); @@ -34,7 +35,20 @@ export class SettingsDocument { // files.defaultLanguage if (location.path[0] === 'files.defaultLanguage') { - return this.provideLanguageCompletionItems(location, range); + return this.provideLanguageCompletionItems(location, range).then(items => { + + // Add special item '${activeEditorLanguage}' + return [this.newSimpleCompletionItem(JSON.stringify('${activeEditorLanguage}'), range, localize('activeEditor', "Use the language of the currently active text editor if any")), ...items]; + }); + } + + // 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); @@ -82,7 +96,7 @@ export class SettingsDocument { })); } else { // Value - return this.provideLanguageCompletionItems(location, range); + return this.provideLanguageCompletionItemsForLanguageOverrides(location, range); } } @@ -153,7 +167,12 @@ export class SettingsDocument { return Promise.resolve(completions); } - private provideLanguageCompletionItems(_location: Location, range: vscode.Range, formatFunc: (string: string) => string = (l) => JSON.stringify(l)): vscode.ProviderResult { + 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(); @@ -178,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); @@ -204,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 d5aafed1189df..36aab5fd224dc 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 3dc984e58c2f0..070312a63e4fa 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/cpp-textmate-grammar", "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", - "commitHash": "d937b80a19706d518e5fe52357d7c50d34a26e60" + "commitHash": "666808cab3907fc91ed4d3901060ee6b045cca58" } }, "license": "MIT", - "version": "1.14.11", + "version": "1.14.15", "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { @@ -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 c3ed8538bd8c8..1c8a244d28596 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 d8c4dca256bab..eef07eeb53d85 100644 --- a/extensions/cpp/syntaxes/c.tmLanguage.json +++ b/extensions/cpp/syntaxes/c.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/jeff-hykin/cpp-textmate-grammar/commit/d937b80a19706d518e5fe52357d7c50d34a26e60", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/72b309aabb63bf14a3cdf0280149121db005d616", "name": "C", "scopeName": "source.c", "patterns": [ @@ -583,7 +583,7 @@ }, "case_statement": { "name": "meta.conditional.case.c", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:[a-zA-Z_]\\w*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!(?:atomic_uint_least64_t|atomic_uint_least16_t|atomic_uint_least32_t|atomic_uint_least8_t|atomic_int_least16_t|atomic_uint_fast64_t|atomic_uint_fast32_t|atomic_int_least64_t|atomic_int_least32_t|pthread_rwlockattr_t|atomic_uint_fast16_t|pthread_mutexattr_t|atomic_int_fast16_t|atomic_uint_fast8_t|atomic_int_fast64_t|atomic_int_least8_t|atomic_int_fast32_t|atomic_int_fast8_t|pthread_condattr_t|atomic_uintptr_t|atomic_ptrdiff_t|pthread_rwlock_t|atomic_uintmax_t|pthread_mutex_t|atomic_intmax_t|atomic_intptr_t|atomic_char32_t|atomic_char16_t|pthread_attr_t|atomic_wchar_t|uint_least64_t|uint_least32_t|uint_least16_t|pthread_cond_t|pthread_once_t|uint_fast64_t|uint_fast16_t|atomic_size_t|uint_least8_t|int_least64_t|int_least32_t|int_least16_t|pthread_key_t|atomic_ullong|atomic_ushort|uint_fast32_t|atomic_schar|atomic_short|uint_fast8_t|int_fast64_t|int_fast32_t|int_fast16_t|atomic_ulong|atomic_llong|int_least8_t|atomic_uchar|memory_order|suseconds_t|int_fast8_t|atomic_bool|atomic_char|atomic_uint|atomic_long|atomic_int|useconds_t|_Imaginary|uintptr_t|pthread_t|in_addr_t|blksize_t|in_port_t|uintmax_t|uintmax_t|blkcnt_t|uint16_t|unsigned|_Complex|uint32_t|intptr_t|intmax_t|intmax_t|uint64_t|u_quad_t|int64_t|int32_t|ssize_t|caddr_t|clock_t|uint8_t|u_short|swblk_t|segsz_t|int16_t|fixpt_t|daddr_t|nlink_t|qaddr_t|size_t|time_t|mode_t|signed|quad_t|ushort|u_long|u_char|double|int8_t|ino_t|uid_t|pid_t|_Bool|float|dev_t|div_t|short|gid_t|off_t|u_int|key_t|id_t|uint|long|void|char|bool|id_t|int)\\b)[a-zA-Z_]\\w*\\b(?!\\())", + "match": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))((?:[a-zA-Z_]\\w*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!(?:atomic_uint_least64_t|atomic_uint_least16_t|atomic_uint_least32_t|atomic_uint_least8_t|atomic_int_least16_t|atomic_uint_fast64_t|atomic_uint_fast32_t|atomic_int_least64_t|atomic_int_least32_t|pthread_rwlockattr_t|atomic_uint_fast16_t|pthread_mutexattr_t|atomic_int_fast16_t|atomic_uint_fast8_t|atomic_int_fast64_t|atomic_int_least8_t|atomic_int_fast32_t|atomic_int_fast8_t|pthread_condattr_t|atomic_uintptr_t|atomic_ptrdiff_t|pthread_rwlock_t|atomic_uintmax_t|pthread_mutex_t|atomic_intmax_t|atomic_intptr_t|atomic_char32_t|atomic_char16_t|pthread_attr_t|atomic_wchar_t|uint_least64_t|uint_least32_t|uint_least16_t|pthread_cond_t|pthread_once_t|uint_fast64_t|uint_fast16_t|atomic_size_t|uint_least8_t|int_least64_t|int_least32_t|int_least16_t|pthread_key_t|atomic_ullong|atomic_ushort|uint_fast32_t|atomic_schar|atomic_short|uint_fast8_t|int_fast64_t|int_fast32_t|int_fast16_t|atomic_ulong|atomic_llong|int_least8_t|atomic_uchar|memory_order|suseconds_t|int_fast8_t|atomic_bool|atomic_char|atomic_uint|atomic_long|atomic_int|useconds_t|_Imaginary|blksize_t|pthread_t|in_addr_t|uintptr_t|in_port_t|uintmax_t|uintmax_t|blkcnt_t|uint16_t|unsigned|_Complex|uint32_t|intptr_t|intmax_t|intmax_t|uint64_t|u_quad_t|int64_t|int32_t|ssize_t|caddr_t|clock_t|uint8_t|u_short|swblk_t|segsz_t|int16_t|fixpt_t|daddr_t|nlink_t|qaddr_t|size_t|time_t|mode_t|signed|quad_t|ushort|u_long|u_char|double|int8_t|ino_t|uid_t|pid_t|_Bool|float|dev_t|div_t|short|gid_t|off_t|u_int|key_t|id_t|uint|long|void|char|bool|id_t|int)\\b)[a-zA-Z_]\\w*\\b(?!\\())", "captures": { "1": { "name": "variable.other.object.access.c" @@ -2772,7 +2772,7 @@ } }, "static_assert": { - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "patterns": [ @@ -3051,7 +3051,7 @@ }, "switch_conditional_parentheses": { "name": "meta.conditional.switch.c", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "patterns": [ @@ -3099,7 +3099,7 @@ }, "switch_statement": { "name": "meta.block.switch.c", - "begin": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(((?:protected|private|public))\\s*(:))", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?:protected|private|public))\\s*(:))", "captures": { "1": { "patterns": [ @@ -194,7 +194,7 @@ }, "alignas_operator": { "contentName": "meta.arguments.operator.alignas.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignas.cpp" @@ -242,7 +242,7 @@ }, "alignof_operator": { "contentName": "meta.arguments.operator.alignof.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignof.cpp" @@ -442,7 +442,7 @@ } }, "builtin_storage_type_initilizer": { - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "patterns": [ @@ -624,7 +624,7 @@ }, "case_statement": { "name": "meta.conditional.case.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.class.cpp" @@ -966,7 +966,7 @@ ] }, "class_declare": { - "match": "(class)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\S)(?![:{])", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", "captures": { "1": { "name": "storage.type.class.declare.cpp" @@ -1006,7 +1006,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -1425,7 +1425,7 @@ }, "constructor_inline": { "name": "meta.function.definition.special.constructor.cpp", - "begin": "(^((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(default)|(delete))", + "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", "captures": { "1": { "name": "keyword.operator.assignment.cpp" @@ -1733,7 +1733,7 @@ }, "constructor_root": { "name": "meta.function.definition.special.constructor.cpp", - "begin": "(\\s*+((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))::((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\16((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\()))", + "begin": "(\\s*+((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", "beginCaptures": { "1": { "name": "meta.head.function.definition.special.constructor.cpp" @@ -1924,7 +1924,7 @@ { "patterns": [ { - "match": "(\\=)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(default)|(delete))", + "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", "captures": { "1": { "name": "keyword.operator.assignment.cpp" @@ -2094,7 +2094,7 @@ ] }, "control_flow_keywords": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\{)", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -2403,7 +2403,7 @@ }, "decltype": { "contentName": "meta.arguments.decltype.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" @@ -2451,7 +2451,7 @@ }, "decltype_specifier": { "contentName": "meta.arguments.decltype.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" @@ -2499,7 +2499,7 @@ }, "default_statement": { "name": "meta.conditional.case.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)(~(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(~(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(default)|(delete))", + "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", "captures": { "1": { "name": "keyword.operator.assignment.cpp" @@ -2773,7 +2773,7 @@ }, "destructor_root": { "name": "meta.function.definition.special.member.destructor.cpp", - "begin": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))::((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))~\\16((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\()))", + "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))~\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", "beginCaptures": { "1": { "name": "meta.head.function.definition.special.member.destructor.cpp" @@ -2964,7 +2964,7 @@ { "patterns": [ { - "match": "(\\=)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(default)|(delete))", + "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", "captures": { "1": { "name": "keyword.operator.assignment.cpp" @@ -3053,7 +3053,7 @@ }, "diagnostic": { "name": "meta.preprocessor.diagnostic.$reference(directive).cpp", - "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*((?:error|warning)))\\b\\s*", + "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:error|warning)))\\b\\s*", "beginCaptures": { "1": { "name": "keyword.control.directive.diagnostic.$7.cpp" @@ -3179,7 +3179,7 @@ }, "enum_block": { "name": "meta.block.enum.cpp", - "begin": "(((?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?(::))?\\s*((?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<15>?)+)>)\\s*)?(::))?\\s*((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\S)(?![:{])", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", "captures": { "1": { "name": "storage.type.enum.declare.cpp" @@ -3347,7 +3347,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -3662,7 +3662,7 @@ ] }, "exception_keywords": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(extern)(?=\\s*\\\"))", + "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(extern)(?=\\s*\\\"))", "beginCaptures": { "1": { "name": "meta.head.extern.cpp" @@ -3859,7 +3859,7 @@ ] }, "function_call": { - "begin": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?(\\()", + "begin": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?(\\()", "beginCaptures": { "1": { "patterns": [ @@ -3933,7 +3933,7 @@ }, "function_definition": { "name": "meta.function.definition.cpp", - "begin": "((?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\())", + "begin": "((?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\())", "beginCaptures": { "1": { "name": "meta.head.function.definition.cpp" @@ -3994,7 +3994,7 @@ "11": { "patterns": [ { - "match": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))", "captures": { "1": { "name": "storage.modifier.$1.cpp" @@ -4228,7 +4228,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -4529,7 +4529,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -4703,7 +4703,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -4843,7 +4843,7 @@ "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -5055,7 +5055,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -5195,7 +5195,7 @@ "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", "captures": { "1": { "name": "keyword.control.goto.cpp" @@ -5335,7 +5335,7 @@ } }, "include": { - "match": "(?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((#)\\s*((?:include|include_next))\\b)\\s*(?:(?:(?:((<)[^>]*(>?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=\\/\\/))))|(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))", + "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((#)\\s*((?:include|include_next))\\b)\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))", "captures": { "1": { "patterns": [ @@ -5535,7 +5535,7 @@ "name": "storage.type.modifier.virtual.cpp" }, { - "match": "(?<=protected|virtual|private|public|,|:)\\s*(?!(?:(?:protected|private|public)|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)\\s*(?!(?:(?:protected|private|public)|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -5734,7 +5734,7 @@ "name": "invalid.illegal.unexpected.punctuation.definition.comment.end.cpp" }, "label": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)", "captures": { "1": { "patterns": [ @@ -5795,7 +5795,7 @@ } }, "lambdas": { - "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[))((?:[^\\]\\[]*\\[.*?\\](?!\\s*\\[)[^\\]\\[]*?)*[^\\]\\[]*?)(\\](?!\\[)))", + "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[| *+\"| *+\\d))((?>(?:[^\\[\\]]|((?(?:(?>[^\\[\\]]*)\\g<4>?)+)\\]))*))(\\](?!\\[)))", "beginCaptures": { "2": { "name": "punctuation.definition.capture.begin.lambda.cpp" @@ -5807,7 +5807,7 @@ "include": "#the_this_keyword" }, { - "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", + "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", "captures": { "1": { "name": "variable.parameter.capture.cpp" @@ -5850,7 +5850,7 @@ } ] }, - "4": { + "5": { "name": "punctuation.definition.capture.end.lambda.cpp" } }, @@ -5919,7 +5919,7 @@ }, "line": { "name": "meta.preprocessor.line.cpp", - "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*line\\b)", + "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*line\\b)", "beginCaptures": { "1": { "name": "keyword.control.directive.line.cpp" @@ -5987,7 +5987,7 @@ }, "macro": { "name": "meta.preprocessor.macro.cpp", - "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*define\\b)\\s*((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*define\\b)\\s*((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!uint_least64_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|double[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|float[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|short[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|char[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|void[^(?-mix:\\w)]|long[^(?-mix:\\w)]|auto[^(?-mix:\\w)]|int[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!uint_least64_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|double[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|float[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|short[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|char[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|void[^(?-mix:\\w)]|long[^(?-mix:\\w)]|auto[^(?-mix:\\w)]|int[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -6124,7 +6124,7 @@ "9": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6166,7 +6166,7 @@ } }, { - "match": "(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))", + "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6221,7 +6221,7 @@ } }, "memory_operators": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(delete)\\s*(\\[\\])|(delete))|(new))(?!\\w))", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:(delete)\\s*(\\[\\])|(delete))|(new))(?!\\w))", "captures": { "1": { "patterns": [ @@ -6266,7 +6266,7 @@ } }, "method_access": { - "begin": "(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", + "begin": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", "beginCaptures": { "1": { "patterns": [ @@ -6308,7 +6308,7 @@ "9": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6350,7 +6350,7 @@ } }, { - "match": "(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))", + "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6423,7 +6423,7 @@ "name": "storage.modifier.$0.cpp" }, "module_import": { - "match": "(?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((import))\\s*(?:(?:(?:((<)[^>]*(>?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=\\/\\/))))|(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))\\s*(;?)", + "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((import))\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))\\s*(;?)", "captures": { "1": { "patterns": [ @@ -6668,7 +6668,7 @@ ] }, "namespace_alias": { - "match": "(?(?:(?>[^<>]*)\\g<9>?)+)>)\\s*)?::)*\\s*+)\\s*((?(?:(?>[^<>]*)\\g<9>?)+)>)\\s*)?::)*\\s*+)\\s*((?(?:(?>[^<>]*)\\g<5>?)+)>)\\s*)?::)*\\s*+)\\s*((?(?:(?>[^<>]*)\\g<5>?)+)>)\\s*)?::)*\\s*+)\\s*((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.noexcept.cpp" @@ -6848,7 +6848,7 @@ ] }, "non_primitive_types": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(operator)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\<|\\())", + "begin": "((?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(operator)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:\\[\\])?)))|(\"\")((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\<|\\())", "beginCaptures": { "1": { "name": "meta.head.function.definition.special.operator-overload.cpp" @@ -7330,7 +7330,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -7606,7 +7606,7 @@ "name": "entity.name.operator.type.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -7935,7 +7935,7 @@ }, "parameter": { "name": "meta.parameter.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\w)", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", "beginCaptures": { "1": { "patterns": [ @@ -7983,7 +7983,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:volatile|register|restrict|static|extern|const))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))+)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=,|\\)|=)", + "match": "((?:((?:volatile|register|restrict|static|extern|const))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -8240,7 +8240,7 @@ "include": "#assignment_operator" }, { - "match": "(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\)|,|\\[|=|\\n)", + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\)|,|\\[|=|\\n)", "captures": { "1": { "patterns": [ @@ -8328,7 +8328,7 @@ "include": "#template_call_range" }, { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*)", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", "captures": { "0": { "patterns": [ @@ -8337,7 +8337,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8428,7 +8428,7 @@ ] }, "parameter_class": { - "match": "(class)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?=,|\\)|\\n)", + "match": "(class)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.class.parameter.cpp" @@ -8493,7 +8493,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8685,7 +8685,7 @@ } }, "parameter_enum": { - "match": "(enum)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?=,|\\)|\\n)", + "match": "(enum)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.enum.parameter.cpp" @@ -8750,7 +8750,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8943,7 +8943,7 @@ }, "parameter_or_maybe_value": { "name": "meta.parameter.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\w)", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", "beginCaptures": { "1": { "patterns": [ @@ -9000,7 +9000,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:volatile|register|restrict|static|extern|const))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))+)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=,|\\)|=)", + "match": "((?:((?:volatile|register|restrict|static|extern|const))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -9257,7 +9257,7 @@ ] }, { - "match": "(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=(?:\\)|,|\\[|=|\\/\\/|(?:\\n|$)))", + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=(?:\\)|,|\\[|=|\\/\\/|(?:\\n|$)))", "captures": { "1": { "patterns": [ @@ -9345,7 +9345,7 @@ "include": "#template_call_range" }, { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*)", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", "captures": { "0": { "patterns": [ @@ -9354,7 +9354,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9448,7 +9448,7 @@ ] }, "parameter_struct": { - "match": "(struct)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?=,|\\)|\\n)", + "match": "(struct)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.struct.parameter.cpp" @@ -9513,7 +9513,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9705,7 +9705,7 @@ } }, "parameter_union": { - "match": "(union)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?=,|\\)|\\n)", + "match": "(union)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.union.parameter.cpp" @@ -9770,7 +9770,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9989,7 +9989,7 @@ ] }, "posix_reserved_types": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*pragma\\b)", + "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\b)", "beginCaptures": { "1": { "name": "keyword.control.directive.pragma.cpp" @@ -10078,7 +10078,7 @@ ] }, "pragma_mark": { - "match": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*pragma\\s+mark)\\s+(.*)", + "match": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\s+mark)\\s+(.*)", "captures": { "1": { "name": "keyword.control.directive.pragma.pragma-mark.cpp" @@ -10202,7 +10202,7 @@ } }, "preprocessor_conditional_range": { - "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*((?:(?:ifndef|ifdef)|if)))", + "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:(?:ifndef|ifdef)|if)))", "beginCaptures": { "1": { "name": "keyword.control.directive.conditional.$7.cpp" @@ -10254,7 +10254,7 @@ ] }, "preprocessor_conditional_standalone": { - "match": "(?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(?![\\w<:.])", "captures": { "0": { "name": "meta.qualified_type.cpp", @@ -10777,7 +10777,7 @@ } }, "qualifiers_and_specifiers_post_parameters": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10833,7 +10833,7 @@ } }, "scope_resolution_function_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10856,7 +10856,7 @@ } }, "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -10893,7 +10893,7 @@ } }, "scope_resolution_function_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10916,7 +10916,7 @@ } }, "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -10953,7 +10953,7 @@ } }, "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -10976,7 +10976,7 @@ } }, "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -11013,7 +11013,7 @@ } }, "scope_resolution_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -11050,7 +11050,7 @@ } }, "scope_resolution_namespace_alias": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11073,7 +11073,7 @@ } }, "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -11110,7 +11110,7 @@ } }, "scope_resolution_namespace_block": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11133,7 +11133,7 @@ } }, "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -11170,7 +11170,7 @@ } }, "scope_resolution_namespace_using": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11193,7 +11193,7 @@ } }, "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -11230,7 +11230,7 @@ } }, "scope_resolution_parameter": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11253,7 +11253,7 @@ } }, "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -11290,7 +11290,7 @@ } }, "scope_resolution_template_call": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11313,7 +11313,7 @@ } }, "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -11350,7 +11350,7 @@ } }, "scope_resolution_template_definition": { - "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", + "match": "(::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<4>?)+)>)\\s*)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -11373,7 +11373,7 @@ } }, "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", + "match": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<8>?)+)>)\\s*)?(::)", "captures": { "1": { "patterns": [ @@ -11414,7 +11414,7 @@ "name": "punctuation.terminator.statement.cpp" }, "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -11588,7 +11588,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -11677,7 +11677,7 @@ } }, "single_line_macro": { - "match": "^((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))#define.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))#define.*(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.cpp" @@ -11766,7 +11766,7 @@ }, "sizeof_variadic_operator": { "contentName": "meta.arguments.operator.sizeof.variadic.cpp", - "begin": "(\\bsizeof\\.\\.\\.)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "(\\bsizeof\\.\\.\\.)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.variadic.cpp" @@ -11852,7 +11852,7 @@ ] }, "static_assert": { - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "patterns": [ @@ -11939,7 +11939,7 @@ ] }, "storage_specifiers": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.struct.cpp" @@ -12450,7 +12450,7 @@ ] }, "struct_declare": { - "match": "(struct)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\S)(?![:{])", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", "captures": { "1": { "name": "storage.type.struct.declare.cpp" @@ -12490,7 +12490,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -12633,7 +12633,7 @@ }, "switch_conditional_parentheses": { "name": "meta.conditional.switch.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "patterns": [ @@ -12681,7 +12681,7 @@ }, "switch_statement": { "name": "meta.block.switch.cpp", - "begin": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", "captures": { "1": { "patterns": [ @@ -13093,7 +13093,7 @@ ] }, "the_this_keyword": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:(?>(?:[^\\*]|(?>\\*+)[^\\/])*)(?:(?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))|(.*(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", + "match": "(using)\\s*(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)(?:(?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))|(.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -13489,7 +13489,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -13620,7 +13620,7 @@ "name": "meta.declaration.type.alias.cpp" }, "type_casting_operators": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.class.cpp" @@ -13950,7 +13950,7 @@ "end": "[\\s\\n]*(?=;)|(?=(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14094,7 +14094,7 @@ "end": "(?<=;)|(?=(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -14268,7 +14268,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14408,7 +14408,7 @@ "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=[{=,);]|\\n)(?!\\()|(?=(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()|(?=(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.struct.cpp" @@ -14778,7 +14778,7 @@ "end": "[\\s\\n]*(?=;)|(?=(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14923,7 +14923,7 @@ "patterns": [ { "name": "meta.block.union.cpp", - "begin": "((((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.union.cpp" @@ -15210,7 +15210,7 @@ "end": "[\\s\\n]*(?=;)|(?=(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -15346,7 +15346,7 @@ }, "typeid_operator": { "contentName": "meta.arguments.operator.typeid.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.typeid.cpp" @@ -15393,7 +15393,7 @@ ] }, "typename": { - "match": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|new|try|for|asm|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -15616,7 +15616,7 @@ } }, "undef": { - "match": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*undef\\b)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*undef\\b)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.union.cpp" @@ -15976,7 +15976,7 @@ ] }, "union_declare": { - "match": "(union)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\S)(?![:{])", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", "captures": { "1": { "name": "storage.type.union.declare.cpp" @@ -16016,7 +16016,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -16167,7 +16167,7 @@ }, "using_namespace": { "name": "meta.using-namespace.cpp", - "begin": "(?(?:(?>[^<>]*)\\g<7>?)+)>)\\s*)?::)*\\s*+)?((?(?:(?>[^<>]*)\\g<7>?)+)>)\\s*)?::)*\\s*+)?((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(((?:protected|private|public))\\s*(:))", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?:protected|private|public))\\s*(:))", "captures": { "1": { "patterns": [ @@ -194,7 +194,7 @@ }, "alignas_operator": { "contentName": "meta.arguments.operator.alignas.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignas.cpp" @@ -242,7 +242,7 @@ }, "alignof_operator": { "contentName": "meta.arguments.operator.alignof.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignof.cpp" @@ -442,7 +442,7 @@ } }, "builtin_storage_type_initilizer": { - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "patterns": [ @@ -624,7 +624,7 @@ }, "case_statement": { "name": "meta.conditional.case.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.class.cpp" @@ -966,7 +966,7 @@ ] }, "class_declare": { - "match": "(class)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\S)(?![:{])", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", "captures": { "1": { "name": "storage.type.class.declare.cpp" @@ -1006,7 +1006,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -1425,7 +1425,7 @@ }, "constructor_inline": { "name": "meta.function.definition.special.constructor.cpp", - "begin": "(^((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(default)|(delete))", + "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", "captures": { "1": { "name": "keyword.operator.assignment.cpp" @@ -1733,7 +1733,7 @@ }, "constructor_root": { "name": "meta.function.definition.special.constructor.cpp", - "begin": "(\\s*+((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))::((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\16((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\()))", + "begin": "(\\s*+((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", "beginCaptures": { "1": { "name": "meta.head.function.definition.special.constructor.cpp" @@ -1924,7 +1924,7 @@ { "patterns": [ { - "match": "(\\=)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(default)|(delete))", + "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", "captures": { "1": { "name": "keyword.operator.assignment.cpp" @@ -2094,7 +2094,7 @@ ] }, "control_flow_keywords": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\{)", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -2403,7 +2403,7 @@ }, "decltype": { "contentName": "meta.arguments.decltype.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" @@ -2451,7 +2451,7 @@ }, "decltype_specifier": { "contentName": "meta.arguments.decltype.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" @@ -2499,7 +2499,7 @@ }, "default_statement": { "name": "meta.conditional.case.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)(~(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:constexpr|explicit|mutable|virtual|inline|friend)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(~(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(default)|(delete))", + "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", "captures": { "1": { "name": "keyword.operator.assignment.cpp" @@ -2773,7 +2773,7 @@ }, "destructor_root": { "name": "meta.function.definition.special.member.destructor.cpp", - "begin": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))::((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))~\\16((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\()))", + "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<14>?)+)>)\\s*)?::)*)(((?>(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))::((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))~\\16((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\()))", "beginCaptures": { "1": { "name": "meta.head.function.definition.special.member.destructor.cpp" @@ -2964,7 +2964,7 @@ { "patterns": [ { - "match": "(\\=)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(default)|(delete))", + "match": "(\\=)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(default)|(delete))", "captures": { "1": { "name": "keyword.operator.assignment.cpp" @@ -3053,7 +3053,7 @@ }, "diagnostic": { "name": "meta.preprocessor.diagnostic.$reference(directive).cpp", - "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*((?:error|warning)))\\b\\s*", + "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:error|warning)))\\b\\s*", "beginCaptures": { "1": { "name": "keyword.control.directive.diagnostic.$7.cpp" @@ -3307,7 +3307,7 @@ ] }, "enum_declare": { - "match": "(enum)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\S)(?![:{])", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", "captures": { "1": { "name": "storage.type.enum.declare.cpp" @@ -3347,7 +3347,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -3662,7 +3662,7 @@ ] }, "exception_keywords": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(extern)(?=\\s*\\\"))", + "begin": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(extern)(?=\\s*\\\"))", "beginCaptures": { "1": { "name": "meta.head.extern.cpp" @@ -3859,7 +3859,7 @@ ] }, "function_call": { - "begin": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?(\\()", + "begin": "((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(((?(?:(?>[^<>]*)\\g<12>?)+)>)\\s*)?(\\()", "beginCaptures": { "1": { "patterns": [ @@ -3933,7 +3933,7 @@ }, "function_definition": { "name": "meta.function.definition.cpp", - "begin": "((?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\())", + "begin": "((?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<70>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\())", "beginCaptures": { "1": { "name": "meta.head.function.definition.cpp" @@ -3994,7 +3994,7 @@ "11": { "patterns": [ { - "match": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))", "captures": { "1": { "name": "storage.modifier.$1.cpp" @@ -4228,7 +4228,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -4529,7 +4529,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -4703,7 +4703,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -4843,7 +4843,7 @@ "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=[{=,);]|\\n)(?!\\()", + "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -4881,7 +4881,7 @@ ] }, "function_pointer_parameter": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -5055,7 +5055,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -5195,7 +5195,7 @@ "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=[{=,);]|\\n)(?!\\()", + "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -5299,7 +5299,7 @@ ] }, "goto_statement": { - "match": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)", "captures": { "1": { "name": "keyword.control.goto.cpp" @@ -5335,7 +5335,7 @@ } }, "include": { - "match": "(?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((#)\\s*((?:include|include_next))\\b)\\s*(?:(?:(?:((<)[^>]*(>?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=\\/\\/))))|(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))", + "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((#)\\s*((?:include|include_next))\\b)\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))", "captures": { "1": { "patterns": [ @@ -5535,7 +5535,7 @@ "name": "storage.type.modifier.virtual.cpp" }, { - "match": "(?<=protected|virtual|private|public|,|:)\\s*(?!(?:(?:protected|private|public)|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)\\s*(?!(?:(?:protected|private|public)|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -5734,7 +5734,7 @@ "name": "invalid.illegal.unexpected.punctuation.definition.comment.end.cpp" }, "label": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)", "captures": { "1": { "patterns": [ @@ -5795,7 +5795,7 @@ } }, "lambdas": { - "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[))((?:[^\\]\\[]*\\[.*?\\](?!\\s*\\[)[^\\]\\[]*?)*[^\\]\\[]*?)(\\](?!\\[)))", + "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[| *+\"| *+\\d))((?>(?:[^\\[\\]]|((?(?:(?>[^\\[\\]]*)\\g<4>?)+)\\]))*))(\\](?!\\[)))", "beginCaptures": { "2": { "name": "punctuation.definition.capture.begin.lambda.cpp" @@ -5807,7 +5807,7 @@ "include": "#the_this_keyword" }, { - "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", + "match": "((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?=\\]|\\z|$)|(,))|(\\=))", "captures": { "1": { "name": "variable.parameter.capture.cpp" @@ -5850,7 +5850,7 @@ } ] }, - "4": { + "5": { "name": "punctuation.definition.capture.end.lambda.cpp" } }, @@ -5919,7 +5919,7 @@ }, "line": { "name": "meta.preprocessor.line.cpp", - "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*line\\b)", + "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*line\\b)", "beginCaptures": { "1": { "name": "keyword.control.directive.line.cpp" @@ -5987,7 +5987,7 @@ }, "macro": { "name": "meta.preprocessor.macro.cpp", - "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*define\\b)\\s*((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*define\\b)\\s*((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!uint_least64_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|double[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|float[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|short[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|char[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|void[^(?-mix:\\w)]|long[^(?-mix:\\w)]|auto[^(?-mix:\\w)]|int[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!uint_least64_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|double[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|float[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|short[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|char[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|void[^(?-mix:\\w)]|long[^(?-mix:\\w)]|auto[^(?-mix:\\w)]|int[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -6124,7 +6124,7 @@ "9": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6166,7 +6166,7 @@ } }, { - "match": "(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))", + "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6221,7 +6221,7 @@ } }, "memory_operators": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(delete)\\s*(\\[\\])|(delete))|(new))(?!\\w))", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?:(delete)\\s*(\\[\\])|(delete))|(new))(?!\\w))", "captures": { "1": { "patterns": [ @@ -6266,7 +6266,7 @@ } }, "method_access": { - "begin": "(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", + "begin": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", "beginCaptures": { "1": { "patterns": [ @@ -6308,7 +6308,7 @@ "9": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6350,7 +6350,7 @@ } }, { - "match": "(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\*|->)))", + "match": "(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6423,7 +6423,7 @@ "name": "storage.modifier.$0.cpp" }, "module_import": { - "match": "(?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((import))\\s*(?:(?:(?:((<)[^>]*(>?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=\\/\\/))))|(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))\\s*(;?)", + "match": "(?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((import))\\s*(?:(?:(?:((<)[^>]*(>?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/)))|((\\\")[^\\\"]*(\\\"?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=\\/\\/))))|(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\.(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)*((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;)))))|((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:\\n|$)|(?=(?:\\/\\/|;))))\\s*(;?)", "captures": { "1": { "patterns": [ @@ -6801,7 +6801,7 @@ }, "noexcept_operator": { "contentName": "meta.arguments.operator.noexcept.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.noexcept.cpp" @@ -6848,7 +6848,7 @@ ] }, "non_primitive_types": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(operator)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\<|\\())", + "begin": "((?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(operator)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<67>?)+)>)\\s*)?::)*)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:\\[\\])?)))|(\"\")((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\<|\\())", "beginCaptures": { "1": { "name": "meta.head.function.definition.special.operator-overload.cpp" @@ -7330,7 +7330,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -7606,7 +7606,7 @@ "name": "entity.name.operator.type.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -7935,7 +7935,7 @@ }, "parameter": { "name": "meta.parameter.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\w)", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", "beginCaptures": { "1": { "patterns": [ @@ -7983,7 +7983,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:volatile|register|restrict|static|extern|const))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))+)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=,|\\)|=)", + "match": "((?:((?:volatile|register|restrict|static|extern|const))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -8240,7 +8240,7 @@ "include": "#assignment_operator" }, { - "match": "(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\)|,|\\[|=|\\n)", + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\)|,|\\[|=|\\n)", "captures": { "1": { "patterns": [ @@ -8328,7 +8328,7 @@ "include": "#template_call_range" }, { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*)", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", "captures": { "0": { "patterns": [ @@ -8337,7 +8337,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8428,7 +8428,7 @@ ] }, "parameter_class": { - "match": "(class)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?=,|\\)|\\n)", + "match": "(class)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.class.parameter.cpp" @@ -8493,7 +8493,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8685,7 +8685,7 @@ } }, "parameter_enum": { - "match": "(enum)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?=,|\\)|\\n)", + "match": "(enum)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.enum.parameter.cpp" @@ -8750,7 +8750,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -8943,7 +8943,7 @@ }, "parameter_or_maybe_value": { "name": "meta.parameter.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\w)", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\w)", "beginCaptures": { "1": { "patterns": [ @@ -9000,7 +9000,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:volatile|register|restrict|static|extern|const))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))+)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=,|\\)|=)", + "match": "((?:((?:volatile|register|restrict|static|extern|const))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))+)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -9257,7 +9257,7 @@ ] }, { - "match": "(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=(?:\\)|,|\\[|=|\\/\\/|(?:\\n|$)))", + "match": "(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=(?:\\)|,|\\[|=|\\/\\/|(?:\\n|$)))", "captures": { "1": { "patterns": [ @@ -9345,7 +9345,7 @@ "include": "#template_call_range" }, { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*)", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*)", "captures": { "0": { "patterns": [ @@ -9354,7 +9354,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9448,7 +9448,7 @@ ] }, "parameter_struct": { - "match": "(struct)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?=,|\\)|\\n)", + "match": "(struct)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.struct.parameter.cpp" @@ -9513,7 +9513,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9705,7 +9705,7 @@ } }, "parameter_union": { - "match": "(union)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:\\[((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\]((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?=,|\\)|\\n)", + "match": "(union)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:\\[((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\]((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?=,|\\)|\\n)", "captures": { "1": { "name": "storage.type.union.parameter.cpp" @@ -9770,7 +9770,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -9989,7 +9989,7 @@ ] }, "posix_reserved_types": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*pragma\\b)", + "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\b)", "beginCaptures": { "1": { "name": "keyword.control.directive.pragma.cpp" @@ -10078,7 +10078,7 @@ ] }, "pragma_mark": { - "match": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*pragma\\s+mark)\\s+(.*)", + "match": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*pragma\\s+mark)\\s+(.*)", "captures": { "1": { "name": "keyword.control.directive.pragma.pragma-mark.cpp" @@ -10202,7 +10202,7 @@ } }, "preprocessor_conditional_range": { - "begin": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*((?:(?:ifndef|ifdef)|if)))", + "begin": "((?:^)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?:(?:ifndef|ifdef)|if)))", "beginCaptures": { "1": { "name": "keyword.control.directive.conditional.$7.cpp" @@ -10254,7 +10254,7 @@ ] }, "preprocessor_conditional_standalone": { - "match": "(?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<26>?)+)>)\\s*)?(?![\\w<:.])", "captures": { "0": { "name": "meta.qualified_type.cpp", @@ -10777,7 +10777,7 @@ } }, "qualifiers_and_specifiers_post_parameters": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -11588,7 +11588,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -11677,7 +11677,7 @@ } }, "single_line_macro": { - "match": "^((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))#define.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))#define.*(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.cpp" @@ -11766,7 +11766,7 @@ }, "sizeof_variadic_operator": { "contentName": "meta.arguments.operator.sizeof.variadic.cpp", - "begin": "(\\bsizeof\\.\\.\\.)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "(\\bsizeof\\.\\.\\.)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.variadic.cpp" @@ -11852,7 +11852,7 @@ ] }, "static_assert": { - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "patterns": [ @@ -11939,7 +11939,7 @@ ] }, "storage_specifiers": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.struct.cpp" @@ -12450,7 +12450,7 @@ ] }, "struct_declare": { - "match": "(struct)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\S)(?![:{])", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", "captures": { "1": { "name": "storage.type.struct.declare.cpp" @@ -12490,7 +12490,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -12633,7 +12633,7 @@ }, "switch_conditional_parentheses": { "name": "meta.conditional.switch.cpp", - "begin": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "patterns": [ @@ -12681,7 +12681,7 @@ }, "switch_statement": { "name": "meta.block.switch.cpp", - "begin": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", + "match": "((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", "captures": { "1": { "patterns": [ @@ -13093,7 +13093,7 @@ ] }, "the_this_keyword": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:(?>(?:[^\\*]|(?>\\*+)[^\\/])*)(?:(?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))|(.*(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", + "match": "(using)\\s*(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)(?:(?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<58>?)+)>)\\s*)?(?![\\w<:.]))|(.*(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -13489,7 +13489,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -13620,7 +13620,7 @@ "name": "meta.declaration.type.alias.cpp" }, "type_casting_operators": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.class.cpp" @@ -13950,7 +13950,7 @@ "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14094,7 +14094,7 @@ "end": "(?<=;)", "patterns": [ { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<27>?)+)>)\\s*)?(?![\\w<:.]))(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -14268,7 +14268,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14408,7 +14408,7 @@ "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" } }, - "end": "(\\))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=[{=,);]|\\n)(?!\\()", + "end": "(\\))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=[{=,);]|\\n)(?!\\()", "endCaptures": { "1": { "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" @@ -14448,7 +14448,7 @@ ] }, "typedef_keyword": { - "match": "((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.struct.cpp" @@ -14778,7 +14778,7 @@ "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -14923,7 +14923,7 @@ "patterns": [ { "name": "meta.block.union.cpp", - "begin": "((((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.union.cpp" @@ -15210,7 +15210,7 @@ "end": "[\\s\\n]*(?=;)", "patterns": [ { - "match": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -15346,7 +15346,7 @@ }, "typeid_operator": { "contentName": "meta.arguments.operator.typeid.cpp", - "begin": "((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "begin": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.typeid.cpp" @@ -15393,7 +15393,7 @@ ] }, "typename": { - "match": "(((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(?![\\w<:.]))", + "match": "(((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(((::)?(?:((?-mix:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_cancel|atomic_commit|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|consteval|constexpr|protected|namespace|co_return|constexpr|constexpr|continue|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|co_await|template|volatile|register|restrict|reflexpr|alignof|private|default|mutable|include|concept|__asm__|defined|_Pragma|alignas|typedef|warning|virtual|mutable|struct|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|while|compl|bitor|const|union|ifdef|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|asm|try|for|new|and|xor|not|do|or|if|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(::))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?!(?:transaction_safe_dynamic|__has_cpp_attribute|transaction_safe|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|dynamic_cast|thread_local|synchronized|static_cast|const_cast|constexpr|consteval|protected|constexpr|co_return|namespace|constexpr|noexcept|typename|decltype|template|operator|noexcept|co_yield|co_await|continue|volatile|register|restrict|explicit|override|volatile|reflexpr|noexcept|requires|default|typedef|nullptr|alignof|mutable|concept|virtual|defined|__asm__|include|_Pragma|mutable|warning|private|alignas|module|switch|not_eq|bitand|and_eq|ifndef|return|sizeof|xor_eq|export|static|delete|public|define|extern|inline|import|pragma|friend|typeid|const|compl|bitor|throw|or_eq|while|catch|break|false|final|const|endif|ifdef|undef|error|using|audit|axiom|line|else|elif|true|NULL|case|goto|else|this|new|asm|not|and|xor|try|for|if|do|or|if)\\b)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(((?(?:(?>[^<>]*)\\g<36>?)+)>)\\s*)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -15616,7 +15616,7 @@ } }, "undef": { - "match": "((?:^)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(#)\\s*undef\\b)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(#)\\s*undef\\b)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(DLLEXPORT)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(final)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(:)((?>[^{]*)))?))", + "begin": "((((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))|((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\))))|(?={))(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(DLLEXPORT)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?:(?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(final)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))?(?:((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(:)((?>[^{]*)))?))", "beginCaptures": { "1": { "name": "meta.head.union.cpp" @@ -15976,7 +15976,7 @@ ] }, "union_declare": { - "match": "(union)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))?(?:(?:\\&|\\*)((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:\\&|\\*))?((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))\\b(?!override\\W|override\\$|final\\W|final\\$)((?\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\S)(?![:{])", + "match": "((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?(?:(?:\\&|\\*)((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))))*(?:\\&|\\*))?((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))\\b(?!override\\W|override\\$|final\\W|final\\$)((?(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))(?=\\S)(?![:{])", "captures": { "1": { "name": "storage.type.union.declare.cpp" @@ -16016,7 +16016,7 @@ "name": "storage.modifier.pointer.cpp" }, { - "match": "(?:\\&((?:(?:(?>\\s+)|(\\/\\*)((?>(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){2,}\\&", + "match": "(?:\\&((?>(?:(?:(?>(?(?:[^\\*]|(?>\\*+)[^\\/])*)((?>\\*+)\\/)))+|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))){2,}\\&", "captures": { "1": { "patterns": [ @@ -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 14fb45016c0e5..0147e6629a2b0 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": "\\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))|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": "\\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": { @@ -461,10 +1005,10 @@ "name": "punctuation.whitespace.support.function.leading" }, "2": { - "name": "support.function.dispatch.c" + "name": "support.function.dispatch.10.14.c" } }, - "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)" + "match": "(\\s*)(\\bdispatch_(?:async_and_wait(?:_f)?|barrier_async_and_wait(?:_f)?|set_qos_class_floor|workloop_(?:create(?:_inactive)?|set_autorelease_frequency))\\b)" }, { "captures": { @@ -472,10 +1016,10 @@ "name": "punctuation.whitespace.support.function.leading" }, "2": { - "name": "support.function.mac-classic.c" + "name": "support.function.dispatch.c" } }, - "match": "(\\s*)(\\bStrLength\\b)" + "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": { @@ -494,10 +1038,21 @@ "name": "punctuation.whitespace.support.function.leading" }, "2": { - "name": "support.function.pthread.10.10.c" + "name": "support.function.quartz.10.11.c" + } + }, + "match": "(\\s*)(\\bCG(?:ColorCreateCopyByMatchingToColorSpace|Event(?:PostToPid|TapCreateForPid)|ImageGetUTType)\\b)" + }, + { + "captures": { + "1": { + "name": "punctuation.whitespace.support.function.leading" + }, + "2": { + "name": "support.function.quartz.10.12.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)" + "match": "(\\s*)(\\bCGColor(?:ConversionInfoCreate(?:FromList)?|Space(?:C(?:opy(?:ICCData|PropertyList)|reateWith(?:ICCData|PropertyList))|IsWideGamutRGB|SupportsOutput))\\b)" }, { "captures": { @@ -505,10 +1060,10 @@ "name": "punctuation.whitespace.support.function.leading" }, "2": { - "name": "support.function.pthread.c" + "name": "support.function.quartz.10.13.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)" + "match": "(\\s*)(\\bCG(?:Color(?:ConversionInfoCreateFromListWithArguments|SpaceGetName)|DataProviderGetInfo|EventCreateScrollWheelEvent2|P(?:DF(?:ContextSetOutline|DocumentGet(?:AccessPermissions|Outline))|athApplyWithBlock))\\b)" }, { "captures": { @@ -516,10 +1071,10 @@ "name": "punctuation.whitespace.support.function.leading" }, "2": { - "name": "support.function.quartz.10.11.c" + "name": "support.function.quartz.10.14.c" } }, - "match": "(\\s*)(\\bCG(?:ColorCreateCopyByMatchingToColorSpace|Event(?:PostToPid|TapCreateForPid)|ImageGetUTType)\\b)" + "match": "(\\s*)(\\bCG(?:ColorConversionInfoCreateWithOptions|ImageGet(?:ByteOrderInfo|PixelFormatInfo)|PDF(?:ArrayApplyBlock|DictionaryApplyBlock))\\b)" }, { "captures": { @@ -527,10 +1082,10 @@ "name": "punctuation.whitespace.support.function.leading" }, "2": { - "name": "support.function.quartz.10.12.c" + "name": "support.function.quartz.10.15.c" } }, - "match": "(\\s*)(\\bCGColor(?:ConversionInfoCreate(?:FromList)?|Space(?:CopyICCData|IsWideGamutRGB|SupportsOutput))\\b)" + "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 0000000000000..b91dec67af647 --- /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-fixtures/test.cpp b/extensions/cpp/test/colorize-fixtures/test.cpp index c3c3c10a953c2..13ec47a554cc4 100644 --- a/extensions/cpp/test/colorize-fixtures/test.cpp +++ b/extensions/cpp/test/colorize-fixtures/test.cpp @@ -16,7 +16,13 @@ void Rectangle::set_values (int x, int y) { height = y; } +long double operator "" _w(long double); +#define MY_MACRO(a, b) + int main () { + 1.2_w; // calls operator "" _w(1.2L) + asm("movl %a %b"); + MY_MACRO(1, 2); Rectangle rect; rect.set_values (3,4); cout << "area: " << rect.area(); diff --git a/extensions/cpp/test/colorize-results/test-23630_cpp.json b/extensions/cpp/test/colorize-results/test-23630_cpp.json index 08d81e6afff9b..4ffc7f01f9878 100644 --- a/extensions/cpp/test/colorize-results/test-23630_cpp.json +++ b/extensions/cpp/test/colorize-results/test-23630_cpp.json @@ -36,10 +36,10 @@ "c": "_UCRT", "t": "source.cpp meta.preprocessor.conditional.cpp entity.name.function.preprocessor.cpp", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "meta.preprocessor: #569CD6", - "light_vs": "meta.preprocessor: #0000FF", + "dark_plus": "entity.name.function.preprocessor: #569CD6", + "light_plus": "entity.name.function.preprocessor: #0000FF", + "dark_vs": "entity.name.function.preprocessor: #569CD6", + "light_vs": "entity.name.function.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA" } }, @@ -91,10 +91,10 @@ "c": "_UCRT", "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "meta.preprocessor: #569CD6", - "light_vs": "meta.preprocessor: #0000FF", + "dark_plus": "entity.name.function.preprocessor: #569CD6", + "light_plus": "entity.name.function.preprocessor: #0000FF", + "dark_vs": "entity.name.function.preprocessor: #569CD6", + "light_vs": "entity.name.function.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA" } }, diff --git a/extensions/cpp/test/colorize-results/test-23850_cpp.json b/extensions/cpp/test/colorize-results/test-23850_cpp.json index 4ba6b59dc9bc6..a97f9612665a8 100644 --- a/extensions/cpp/test/colorize-results/test-23850_cpp.json +++ b/extensions/cpp/test/colorize-results/test-23850_cpp.json @@ -36,10 +36,10 @@ "c": "_UCRT", "t": "source.cpp meta.preprocessor.conditional.cpp entity.name.function.preprocessor.cpp", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "meta.preprocessor: #569CD6", - "light_vs": "meta.preprocessor: #0000FF", + "dark_plus": "entity.name.function.preprocessor: #569CD6", + "light_plus": "entity.name.function.preprocessor: #0000FF", + "dark_vs": "entity.name.function.preprocessor: #569CD6", + "light_vs": "entity.name.function.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA" } }, @@ -80,10 +80,10 @@ "c": "_UCRT", "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "meta.preprocessor: #569CD6", - "light_vs": "meta.preprocessor: #0000FF", + "dark_plus": "entity.name.function.preprocessor: #569CD6", + "light_plus": "entity.name.function.preprocessor: #0000FF", + "dark_vs": "entity.name.function.preprocessor: #569CD6", + "light_vs": "entity.name.function.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA" } }, diff --git a/extensions/cpp/test/colorize-results/test-78769_cpp.json b/extensions/cpp/test/colorize-results/test-78769_cpp.json index 1d82d5c4e43e4..8284688060362 100644 --- a/extensions/cpp/test/colorize-results/test-78769_cpp.json +++ b/extensions/cpp/test/colorize-results/test-78769_cpp.json @@ -36,10 +36,10 @@ "c": "DOCTEST_IMPLEMENT_FIXTURE", "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "meta.preprocessor: #569CD6", - "light_vs": "meta.preprocessor: #0000FF", + "dark_plus": "entity.name.function.preprocessor: #569CD6", + "light_plus": "entity.name.function.preprocessor: #0000FF", + "dark_vs": "entity.name.function.preprocessor: #569CD6", + "light_vs": "entity.name.function.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA" } }, @@ -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-80644_cpp.json b/extensions/cpp/test/colorize-results/test-80644_cpp.json index 580591726a45e..069f31ee72bb6 100644 --- a/extensions/cpp/test/colorize-results/test-80644_cpp.json +++ b/extensions/cpp/test/colorize-results/test-80644_cpp.json @@ -378,9 +378,9 @@ "t": "source.cpp meta.function.definition.special.constructor.cpp meta.head.function.definition.special.constructor.cpp meta.parameter.initialization.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -477,9 +477,9 @@ "t": "source.cpp meta.function.definition.special.constructor.cpp meta.head.function.definition.special.constructor.cpp meta.parameter.initialization.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -527,4 +527,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] 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 0000000000000..fcc9e47435bf6 --- /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_c.json b/extensions/cpp/test/colorize-results/test_c.json index 74be734c5ca9f..30fd5393c476a 100644 --- a/extensions/cpp/test/colorize-results/test_c.json +++ b/extensions/cpp/test/colorize-results/test_c.json @@ -851,9 +851,9 @@ "t": "source.c meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -983,9 +983,9 @@ "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1181,9 +1181,9 @@ "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1390,9 +1390,9 @@ "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1720,9 +1720,9 @@ "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1863,9 +1863,9 @@ "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2226,9 +2226,9 @@ "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2391,9 +2391,9 @@ "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2765,9 +2765,9 @@ "t": "source.c meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2793,4 +2793,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/cpp/test/colorize-results/test_cc.json b/extensions/cpp/test/colorize-results/test_cc.json index 902ef645164b7..324b231bb1bf1 100644 --- a/extensions/cpp/test/colorize-results/test_cc.json +++ b/extensions/cpp/test/colorize-results/test_cc.json @@ -36,10 +36,10 @@ "c": "B4G_DEBUG_CHECK", "t": "source.cpp meta.preprocessor.conditional.cpp entity.name.function.preprocessor.cpp", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "meta.preprocessor: #569CD6", - "light_vs": "meta.preprocessor: #0000FF", + "dark_plus": "entity.name.function.preprocessor: #569CD6", + "light_plus": "entity.name.function.preprocessor: #0000FF", + "dark_vs": "entity.name.function.preprocessor: #569CD6", + "light_vs": "entity.name.function.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA" } }, @@ -268,9 +268,9 @@ "t": "source.cpp meta.parens.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1807,10 +1807,10 @@ "c": "movw $0x38, %ax; ltr %ax", "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly.cpp", "r": { - "dark_plus": "meta.embedded: #D4D4D4", - "light_plus": "meta.embedded: #000000", - "dark_vs": "meta.embedded: #D4D4D4", - "light_vs": "meta.embedded: #000000", + "dark_plus": "meta.embedded.assembly: #CE9178", + "light_plus": "meta.embedded.assembly: #A31515", + "dark_vs": "meta.embedded.assembly: #CE9178", + "light_vs": "meta.embedded.assembly: #A31515", "hc_black": "meta.embedded: #FFFFFF" } }, @@ -1979,4 +1979,4 @@ "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 f84d916afa3ab..10f6041bb0004 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -190,10 +190,10 @@ "c": "EXTERN_C", "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { - "dark_plus": "entity.name.function: #DCDCAA", - "light_plus": "entity.name.function: #795E26", - "dark_vs": "meta.preprocessor: #569CD6", - "light_vs": "meta.preprocessor: #0000FF", + "dark_plus": "entity.name.function.preprocessor: #569CD6", + "light_plus": "entity.name.function.preprocessor: #0000FF", + "dark_vs": "entity.name.function.preprocessor: #569CD6", + "light_vs": "entity.name.function.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA" } }, @@ -1011,6 +1011,281 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "long", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.qualified_type.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.function.definition.special.operator-overload.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": "double", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.qualified_type.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.function.definition.special.operator-overload.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "operator", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp keyword.other.operator.overload.cpp", + "r": { + "dark_plus": "keyword.other.operator: #C586C0", + "light_plus": "keyword.other.operator: #AF00DB", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword.other.operator: #C586C0" + } + }, + { + "c": " ", + "t": "source.cpp meta.function.definition.special.operator-overload.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.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp entity.name.operator.custom-literal.cpp", + "r": { + "dark_plus": "entity.name.operator.custom-literal: #DCDCAA", + "light_plus": "entity.name.operator.custom-literal: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "_w", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp entity.name.operator.custom-literal.cpp", + "r": { + "dark_plus": "entity.name.operator.custom-literal: #DCDCAA", + "light_plus": "entity.name.operator.custom-literal: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp punctuation.section.parameters.begin.bracket.round.special.operator-overload.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 meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.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.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "double", + "t": "source.cpp meta.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp meta.function.definition.parameters.special.operator-overload.cpp meta.parameter.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.function.definition.special.operator-overload.cpp meta.head.function.definition.special.operator-overload.cpp punctuation.section.parameters.end.bracket.round.special.operator-overload.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.terminator.statement.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.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", + "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": "define", + "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", + "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.cpp meta.preprocessor.macro.cpp", + "r": { + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6" + } + }, + { + "c": "MY_MACRO", + "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", + "r": { + "dark_plus": "entity.name.function.preprocessor: #569CD6", + "light_plus": "entity.name.function.preprocessor: #0000FF", + "dark_vs": "entity.name.function.preprocessor: #569CD6", + "light_vs": "entity.name.function.preprocessor: #0000FF", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "source.cpp meta.preprocessor.macro.cpp punctuation.definition.parameters.begin.preprocessor.cpp", + "r": { + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6" + } + }, + { + "c": "a", + "t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ",", + "t": "source.cpp meta.preprocessor.macro.cpp punctuation.separator.parameters.cpp", + "r": { + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6" + } + }, + { + "c": " ", + "t": "source.cpp meta.preprocessor.macro.cpp", + "r": { + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6" + } + }, + { + "c": "b", + "t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.cpp meta.preprocessor.macro.cpp punctuation.definition.parameters.end.preprocessor.cpp", + "r": { + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6" + } + }, { "c": "int", "t": "source.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", @@ -1099,6 +1374,292 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": " ", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.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.function.definition.cpp meta.body.function.definition.cpp constant.numeric.decimal.point.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": "2", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.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": "_w", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.other.unit.user-defined.cpp", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #098658", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #098658", + "hc_black": "keyword.other.unit: #B5CEA8" + } + }, + { + "c": ";", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.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.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "//", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " calls operator \"\" _w(1.2L)", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " ", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "asm", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp storage.type.asm.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.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp punctuation.section.parens.begin.bracket.round.assembly.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.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp punctuation.definition.string.begin.assembly.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "movl %a %b", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly.cpp", + "r": { + "dark_plus": "meta.embedded.assembly: #CE9178", + "light_plus": "meta.embedded.assembly: #A31515", + "dark_vs": "meta.embedded.assembly: #CE9178", + "light_vs": "meta.embedded.assembly: #A31515", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp punctuation.definition.string.end.assembly.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp punctuation.section.parens.end.bracket.round.assembly.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.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.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.function.definition.cpp meta.body.function.definition.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "MY_MACRO", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.call.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 meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "1", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.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.function.definition.cpp meta.body.function.definition.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 meta.function.definition.cpp meta.body.function.definition.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "2", + "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.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.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.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.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": " Rectangle rect", "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", @@ -1192,9 +1753,9 @@ "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1214,9 +1775,9 @@ "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1588,9 +2149,9 @@ "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1687,9 +2248,9 @@ "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.parens.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1753,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" @@ -1841,9 +2402,9 @@ "t": "source.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index beef7f27b8f2c..6156bbb508282 100644 --- a/extensions/csharp/cgmanifest.json +++ b/extensions/csharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/csharp-tmLanguage", "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", - "commitHash": "ad7514e8d78542a6ee37f6187091cd4102eb3797" + "commitHash": "572697a2c2267430010b3534281f73337144e94f" } }, "license": "MIT", diff --git a/extensions/csharp/package.json b/extensions/csharp/package.json index 3841eabd13275..74bbb2837883c 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/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index 4d80bcc364e5b..8e26aaefa775c 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.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/dotnet/csharp-tmLanguage/commit/ad7514e8d78542a6ee37f6187091cd4102eb3797", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/572697a2c2267430010b3534281f73337144e94f", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -2498,7 +2498,7 @@ }, "interpolation": { "name": "meta.interpolation.cs", - "begin": "(?<=[^\\{])((?:\\{\\{)*)(\\{)(?=[^\\{])", + "begin": "(?<=[^\\{]|^)((?:\\{\\{)*)(\\{)(?=[^\\{])", "beginCaptures": { "1": { "name": "string.quoted.double.cs" diff --git a/extensions/csharp/test/colorize-results/test_cs.json b/extensions/csharp/test/colorize-results/test_cs.json index dbbe61ef3c645..5893af16e9db3 100644 --- a/extensions/csharp/test/colorize-results/test_cs.json +++ b/extensions/csharp/test/colorize-results/test_cs.json @@ -444,9 +444,9 @@ "t": "source.cs constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -477,9 +477,9 @@ "t": "source.cs constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -510,9 +510,9 @@ "t": "source.cs constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -543,9 +543,9 @@ "t": "source.cs constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -576,9 +576,9 @@ "t": "source.cs constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -719,9 +719,9 @@ "t": "source.cs constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1027,9 +1027,9 @@ "t": "source.cs constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1374,4 +1374,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/css-language-features/.vscodeignore b/extensions/css-language-features/.vscodeignore index fa38a47136231..a08d9b8dec775 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/client/src/browser/cssClientMain.ts b/extensions/css-language-features/client/src/browser/cssClientMain.ts new file mode 100644 index 0000000000000..2a5e3e1f2c24e --- /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/cssClient.ts b/extensions/css-language-features/client/src/cssClient.ts new file mode 100644 index 0000000000000..4bfa95c843987 --- /dev/null +++ b/extensions/css-language-features/client/src/cssClient.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 { 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 { getCustomDataSource } from './customData'; +import { RequestService, serveFileSystemRequests } from './requests'; + +namespace CustomDataChangedNotification { + export const type: NotificationType = new NotificationType('css/customDataChanged'); +} + +const localize = nls.loadMessageBundle(); + +export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => CommonLanguageClient; + +export interface Runtime { + TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string; } }; + fs?: RequestService; +} + +export function startClient(context: ExtensionContext, newLanguageClient: LanguageClientConstructor, runtime: Runtime) { + + const customDataSource = getCustomDataSource(context.subscriptions); + + let documentSelector = ['css', 'scss', 'less']; + + // Options to control the language client + let clientOptions: LanguageClientOptions = { + documentSelector, + synchronize: { + configurationSection: ['css', 'scss', 'less'] + }, + initializationOptions: { + handledSchemas: ['file'] + }, + middleware: { + provideCompletionItem(document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: ProvideCompletionItemsSignature): ProviderResult { + // testing the replace / insert mode + 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 updateLabel(item: CompletionItem) { + if (item.kind === CompletionItemKind.Color) { + item.label2 = { + name: item.label, + type: (item.documentation as string) + }; + } + } + // testing the new completion + function updateProposals(r: CompletionItem[] | CompletionList | null | undefined): CompletionItem[] | CompletionList | null | undefined { + if (r) { + (Array.isArray(r) ? r : r.items).forEach(updateRanges); + (Array.isArray(r) ? r : r.items).forEach(updateLabel); + } + 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 = 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 + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); + + let indentationRules = { + increaseIndentPattern: /(^.*\{[^}]*$)/, + decreaseIndentPattern: /^\s*\}/ + }; + + languages.setLanguageConfiguration('css', { + wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g, + indentationRules: indentationRules + }); + + languages.setLanguageConfiguration('less', { + wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]+(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g, + indentationRules: indentationRules + }); + + languages.setLanguageConfiguration('scss', { + wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@$#.!])?[\w-?]+%?|[@#!$.])/g, + indentationRules: indentationRules + }); + + client.onReady().then(() => { + context.subscriptions.push(initCompletionProvider()); + }); + + function initCompletionProvider(): Disposable { + const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/; + + return languages.registerCompletionItemProvider(documentSelector, { + provideCompletionItems(doc: TextDocument, pos: Position) { + let lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos)); + let match = lineUntilPos.match(regionCompletionRegExpr); + if (match) { + let range = new Range(new Position(pos.line, match[1].length), pos); + let beginProposal = new CompletionItem('#region', CompletionItemKind.Snippet); + beginProposal.range = range; TextEdit.replace(range, '/* #region */'); + beginProposal.insertText = new SnippetString('/* #region $1*/'); + beginProposal.documentation = localize('folding.start', 'Folding Region Start'); + beginProposal.filterText = match[2]; + beginProposal.sortText = 'za'; + let endProposal = new CompletionItem('#endregion', CompletionItemKind.Snippet); + endProposal.range = range; + endProposal.insertText = '/* #endregion */'; + endProposal.documentation = localize('folding.end', 'Folding Region End'); + endProposal.sortText = 'zb'; + endProposal.filterText = match[2]; + return [beginProposal, endProposal]; + } + return null; + } + }); + } + + commands.registerCommand('_css.applyCodeAction', applyCodeAction); + + function applyCodeAction(uri: string, documentVersion: number, edits: TextEdit[]) { + let textEditor = window.activeTextEditor; + if (textEditor && textEditor.document.uri.toString() === uri) { + if (textEditor.document.version !== documentVersion) { + window.showInformationMessage(`CSS fix is outdated and can't be applied to the document.`); + } + textEditor.edit(mutator => { + for (let edit of edits) { + mutator.replace(client.protocol2CodeConverter.asRange(edit.range), edit.newText); + } + }).then(success => { + if (!success) { + window.showErrorMessage('Failed to apply CSS fix to the document. Please consider opening an issue with steps to reproduce.'); + } + }); + } + } +} diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts deleted file mode 100644 index 1bc18bf634628..0000000000000 --- a/extensions/css-language-features/client/src/cssMain.ts +++ /dev/null @@ -1,139 +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 * as path from 'path'; -import { commands, CompletionItem, CompletionItemKind, ExtensionContext, languages, Position, Range, SnippetString, TextEdit, window, workspace } from 'vscode'; -import { Disposable, LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient'; -import * as nls from 'vscode-nls'; -import { getCustomDataPathsFromAllExtensions, getCustomDataPathsInAllWorkspaces } from './customData'; - -const localize = nls.loadMessageBundle(); - -// this method is called when vs code is activated -export function activate(context: ExtensionContext) { - - 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=6044'] }; - - // 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 = ['css', 'scss', 'less']; - - let dataPaths = [ - ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders), - ...getCustomDataPathsFromAllExtensions() - ]; - - // Options to control the language client - let clientOptions: LanguageClientOptions = { - documentSelector, - synchronize: { - configurationSection: ['css', 'scss', 'less'] - }, - initializationOptions: { - dataPaths - } - }; - - // Create the language client and start the client. - let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions); - client.registerProposedFeatures(); - - let disposable = client.start(); - // Push the disposable to the context's subscriptions so that the - // client can be deactivated on extension deactivation - context.subscriptions.push(disposable); - - let indentationRules = { - increaseIndentPattern: /(^.*\{[^}]*$)/, - decreaseIndentPattern: /^\s*\}/ - }; - - languages.setLanguageConfiguration('css', { - wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g, - indentationRules: indentationRules - }); - - languages.setLanguageConfiguration('less', { - wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]+(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g, - indentationRules: indentationRules - }); - - languages.setLanguageConfiguration('scss', { - wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@$#.!])?[\w-?]+%?|[@#!$.])/g, - indentationRules: indentationRules - }); - - client.onReady().then(() => { - context.subscriptions.push(initCompletionProvider()); - }); - - function initCompletionProvider(): Disposable { - const regionCompletionRegExpr = /^(\s*)(\/(\*\s*(#\w*)?)?)?$/; - - return languages.registerCompletionItemProvider(documentSelector, { - provideCompletionItems(doc, pos) { - let lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos)); - let match = lineUntilPos.match(regionCompletionRegExpr); - if (match) { - let range = new Range(new Position(pos.line, match[1].length), pos); - let beginProposal = new CompletionItem('#region', CompletionItemKind.Snippet); - beginProposal.range = range; TextEdit.replace(range, '/* #region */'); - beginProposal.insertText = new SnippetString('/* #region $1*/'); - beginProposal.documentation = localize('folding.start', 'Folding Region Start'); - beginProposal.filterText = match[2]; - beginProposal.sortText = 'za'; - let endProposal = new CompletionItem('#endregion', CompletionItemKind.Snippet); - endProposal.range = range; - endProposal.insertText = '/* #endregion */'; - endProposal.documentation = localize('folding.end', 'Folding Region End'); - endProposal.sortText = 'zb'; - endProposal.filterText = match[2]; - return [beginProposal, endProposal]; - } - return null; - } - }); - } - - commands.registerCommand('_css.applyCodeAction', applyCodeAction); - - function applyCodeAction(uri: string, documentVersion: number, edits: TextEdit[]) { - let textEditor = window.activeTextEditor; - if (textEditor && textEditor.document.uri.toString() === uri) { - if (textEditor.document.version !== documentVersion) { - window.showInformationMessage(`CSS fix is outdated and can't be applied to the document.`); - } - textEditor.edit(mutator => { - for (let edit of edits) { - mutator.replace(client.protocol2CodeConverter.asRange(edit.range), edit.newText); - } - }).then(success => { - if (!success) { - window.showErrorMessage('Failed to apply CSS fix to the document. Please consider opening an issue with steps to reproduce.'); - } - }); - } - } -} - -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 7054d0f1ba96d..24b56f12b0612 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 0000000000000..cc675b494c2b3 --- /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 0000000000000..c13ef2e1c08d5 --- /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 0000000000000..1b1e70b2d882b --- /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 0000000000000..cb2e13c7ed316 --- /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 dec7ad5afb4a0..a931210ab32ea 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 702d9d755cad0..0b632903b52ff 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", @@ -783,6 +784,17 @@ } } ], + "configurationDefaults": { + "[css]": { + "editor.suggest.insertMode": "replace" + }, + "[scss]": { + "editor.suggest.insertMode": "replace" + }, + "[less]": { + "editor.suggest.insertMode": "replace" + } + }, "jsonValidation": [ { "fileMatch": "*.css-data.json", @@ -795,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/schemas/package.schema.json b/extensions/css-language-features/schemas/package.schema.json index 3aeca1e040a08..cf4193008ec59 100644 --- a/extensions/css-language-features/schemas/package.schema.json +++ b/extensions/css-language-features/schemas/package.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "title": "CSS contributions to package.json", "type": "object", "properties": { 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 0000000000000..38816259ddf9c --- /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 68b850b377320..531035f636ca1 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 6d4265225396c..331af0883a934 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.19", - "vscode-languageserver": "^6.0.0-next.3" + "vscode-css-languageservice": "^4.3.3", + "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 0000000000000..13284fadcd956 --- /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 0000000000000..c2666a46c50ed --- /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 6a9db0b77e779..0000000000000 --- 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 f173d884a2b24..ccfc706452c27 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 0000000000000..9e145398ff15b --- /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 0000000000000..c7b1301296d4a --- /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 6862f28e03489..0000000000000 --- 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 ', + '', + '', + '\t$0', + '', + ''].join('\n'); + snippetProposal.insertText = new SnippetString(content); + snippetProposal.documentation = localize('folding.html', 'Simple HTML5 starting point'); + snippetProposal.filterText = match2[2]; + snippetProposal.sortText = 'za'; + results.push(snippetProposal); + } + return results; + } + }); + + const promptForTypeOnRenameKey = 'html.promptForTypeOnRename'; + const promptForTypeOnRename = extensions.getExtension('formulahendry.auto-rename-tag') !== undefined && + (context.globalState.get(promptForTypeOnRenameKey) !== false) && + !workspace.getConfiguration('editor', { languageId: 'html' }).get('renameOnType'); + + if (promptForTypeOnRename) { + const activeEditorListener = window.onDidChangeActiveTextEditor(async e => { + if (e && documentSelector.indexOf(e.document.languageId) !== -1) { + context.globalState.update(promptForTypeOnRenameKey, false); + activeEditorListener.dispose(); + const configure = localize('configureButton', 'Configure'); + const res = await window.showInformationMessage(localize('renameOnTypeQuestion', 'VS Code now has built-in support for auto-renaming tags. Do you want to enable it?'), configure); + if (res === configure) { + commands.executeCommand('workbench.action.openSettings', SettingIds.renameOnType); + } + } + }); + toDispose.push(activeEditorListener); + } + + toDispose.push(); +} diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts deleted file mode 100644 index 89bdc325721b6..0000000000000 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ /dev/null @@ -1,237 +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'; -const localize = nls.loadMessageBundle(); - -import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, workspace, Disposable, FormattingOptions, CancellationToken, ProviderResult, TextEdit } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } from 'vscode-languageclient'; -import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; -import { activateTagClosing } from './tagClosing'; -import TelemetryReporter from 'vscode-extension-telemetry'; -import { getCustomDataPathsInAllWorkspaces, getCustomDataPathsFromAllExtensions } from './customData'; - -namespace TagCloseRequest { - export const type: RequestType = new RequestType('html/tag'); -} - -interface IPackageInfo { - name: string; - version: string; - aiKey: string; -} - -let telemetryReporter: TelemetryReporter | null; - - -export function activate(context: ExtensionContext) { - let toDispose = context.subscriptions; - - 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=6045'] }; - - // 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 = ['html', 'handlebars']; - let embeddedLanguages = { css: true, javascript: true }; - - let rangeFormatting: Disposable | undefined = undefined; - - let dataPaths = [ - ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders), - ...getCustomDataPathsFromAllExtensions() - ]; - - // Options to control the language client - let clientOptions: LanguageClientOptions = { - documentSelector, - synchronize: { - configurationSection: ['html', 'css', 'javascript'], // the settings to synchronize - }, - initializationOptions: { - embeddedLanguages, - dataPaths, - provideFormatter: false, // tell the server to not provide formatting capability and ignore the `html.format.enable` setting. - }, - }; - - // Create the language client and start the client. - let client = new LanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), serverOptions, clientOptions); - client.registerProposedFeatures(); - - let disposable = client.start(); - toDispose.push(disposable); - client.onReady().then(() => { - let tagRequestor = (document: TextDocument, position: Position) => { - let param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position); - return client.sendRequest(TagCloseRequest.type, param); - }; - disposable = activateTagClosing(tagRequestor, { html: true, handlebars: true }, 'html.autoClosingTags'); - toDispose.push(disposable); - - disposable = client.onTelemetry(e => { - if (telemetryReporter) { - telemetryReporter.sendTelemetryEvent(e.key, e.data); - } - }); - toDispose.push(disposable); - - // 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())); - }); - - function updateFormatterRegistration() { - const formatEnabled = workspace.getConfiguration().get('html.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([]); - } - ); - } - }); - } - } - - languages.setLanguageConfiguration('html', { - indentationRules: { - increaseIndentPattern: /<(?!\?|(?:area|base|br|col|frame|hr|html|img|input|link|meta|param)\b|[^>]*\/>)([-_\.A-Za-z0-9]+)(?=\s|>)\b[^>]*>(?!.*<\/\1>)|)|\{[^}"']*$/, - decreaseIndentPattern: /^\s*(<\/(?!html)[-_\.A-Za-z0-9]+\b[^>]*>|-->|\})/ - }, - wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g, - onEnterRules: [ - { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>/i, - action: { indentAction: IndentAction.IndentOutdent } - }, - { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - action: { indentAction: IndentAction.Indent } - } - ], - }); - - languages.setLanguageConfiguration('handlebars', { - wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g, - onEnterRules: [ - { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>/i, - action: { indentAction: IndentAction.IndentOutdent } - }, - { - beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'), - action: { indentAction: IndentAction.Indent } - } - ], - }); - - const regionCompletionRegExpr = /^(\s*)(<(!(-(-\s*(#\w*)?)?)?)?)?$/; - const htmlSnippetCompletionRegExpr = /^(\s*)(<(h(t(m(l)?)?)?)?)?$/; - languages.registerCompletionItemProvider(documentSelector, { - provideCompletionItems(doc, pos) { - const results: CompletionItem[] = []; - let lineUntilPos = doc.getText(new Range(new Position(pos.line, 0), pos)); - let match = lineUntilPos.match(regionCompletionRegExpr); - if (match) { - let range = new Range(new Position(pos.line, match[1].length), pos); - let beginProposal = new CompletionItem('#region', CompletionItemKind.Snippet); - beginProposal.range = range; - beginProposal.insertText = new SnippetString(''); - beginProposal.documentation = localize('folding.start', 'Folding Region Start'); - beginProposal.filterText = match[2]; - beginProposal.sortText = 'za'; - results.push(beginProposal); - let endProposal = new CompletionItem('#endregion', CompletionItemKind.Snippet); - endProposal.range = range; - endProposal.insertText = new SnippetString(''); - endProposal.documentation = localize('folding.end', 'Folding Region End'); - endProposal.filterText = match[2]; - endProposal.sortText = 'zb'; - results.push(endProposal); - } - let match2 = lineUntilPos.match(htmlSnippetCompletionRegExpr); - if (match2 && doc.getText(new Range(new Position(0, 0), pos)).match(htmlSnippetCompletionRegExpr)) { - let range = new Range(new Position(pos.line, match2[1].length), pos); - let snippetProposal = new CompletionItem('HTML sample', CompletionItemKind.Snippet); - snippetProposal.range = range; - const content = ['', - '', - '', - '\t', - '\t', - '\t${1:Page Title}', - '\t', - '\t', - '\t', - '', - '', - '\t$0', - '', - ''].join('\n'); - snippetProposal.insertText = new SnippetString(content); - snippetProposal.documentation = localize('folding.html', 'Simple HTML5 starting point'); - snippetProposal.filterText = match2[2]; - snippetProposal.sortText = 'za'; - results.push(snippetProposal); - } - return results; - } - }); -} - -function getPackageInfo(context: ExtensionContext): IPackageInfo | null { - let extensionPackage = readJSONFile(context.asAbsolutePath('./package.json')); - if (extensionPackage) { - return { - name: extensionPackage.name, - version: extensionPackage.version, - aiKey: extensionPackage.aiKey - }; - } - return null; -} - - -function readJSONFile(location: string) { - try { - return JSON.parse(fs.readFileSync(location).toString()); - } catch (e) { - console.log(`Problems reading ${location}: ${e}`); - return {}; - } -} - -export function deactivate(): Promise { - return telemetryReporter ? telemetryReporter.dispose() : Promise.resolve(null); -} diff --git a/extensions/html-language-features/client/src/node/htmlClientMain.ts b/extensions/html-language-features/client/src/node/htmlClientMain.ts new file mode 100644 index 0000000000000..c58515b371221 --- /dev/null +++ b/extensions/html-language-features/client/src/node/htmlClientMain.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. + *--------------------------------------------------------------------------------------------*/ + +import { getNodeFSRequestService } from './nodeFs'; +import { ExtensionContext } from 'vscode'; +import { startClient, LanguageClientConstructor } from '../htmlClient'; +import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; +import { TextDecoder } from 'util'; +import * as fs from 'fs'; +import TelemetryReporter from 'vscode-extension-telemetry'; + + +let telemetry: TelemetryReporter | undefined; + +// this method is called when vs code is activated +export function activate(context: ExtensionContext) { + + let clientPackageJSON = getPackageInfo(context); + telemetry = new TelemetryReporter(clientPackageJSON.name, clientPackageJSON.version, clientPackageJSON.aiKey); + + const serverMain = `./server/${clientPackageJSON.main.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/htmlServerMain`; + 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, telemetry }); +} + +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: '' }; + } +} diff --git a/extensions/html-language-features/client/src/node/nodeFs.ts b/extensions/html-language-features/client/src/node/nodeFs.ts new file mode 100644 index 0000000000000..c13ef2e1c08d5 --- /dev/null +++ b/extensions/html-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/html-language-features/client/src/requests.ts b/extensions/html-language-features/client/src/requests.ts new file mode 100644 index 0000000000000..ac966636314c4 --- /dev/null +++ b/extensions/html-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 './htmlClient'; + +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/html-language-features/extension-browser.webpack.config.js b/extensions/html-language-features/extension-browser.webpack.config.js new file mode 100644 index 0000000000000..9988ab826aaf4 --- /dev/null +++ b/extensions/html-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/htmlClientMain.ts' + }, + output: { + filename: 'htmlClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'browser') + } +}); diff --git a/extensions/html-language-features/extension.webpack.config.js b/extensions/html-language-features/extension.webpack.config.js index 9624295ff5b7b..6af444db93064 100644 --- a/extensions/html-language-features/extension.webpack.config.js +++ b/extensions/html-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/htmlMain.ts', + extension: './src/node/htmlClientMain.ts', }, output: { - filename: 'htmlMain.js', - path: path.join(__dirname, 'client', 'dist') + filename: 'htmlClientMain.js', + path: path.join(__dirname, 'client', 'dist', 'node') } }); diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 7c4f83d6b5b56..e3cf08de8caac 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -15,7 +15,8 @@ "onLanguage:html", "onLanguage:handlebars" ], - "main": "./client/out/htmlMain", + "main": "./client/out/node/htmlClientMain", + "browser": "./client/dist/browser/htmlClientMain", "scripts": { "compile": "npx gulp compile-extension:html-language-features-client compile-extension:html-language-features-server", "watch": "npx gulp watch-extension:html-language-features-client watch-extension:html-language-features-server", @@ -161,6 +162,13 @@ "default": true, "description": "%html.autoClosingTags%" }, + "html.mirrorCursorOnMatchingTag": { + "type": "boolean", + "scope": "resource", + "default": false, + "description": "%html.mirrorCursorOnMatchingTag%", + "deprecationMessage": "%html.mirrorCursorOnMatchingTagDeprecationMessage%" + }, "html.trace.server": { "type": "string", "scope": "window", @@ -174,6 +182,14 @@ } } }, + "configurationDefaults": { + "[html]": { + "editor.suggest.insertMode": "replace" + }, + "[handlebars]": { + "editor.suggest.insertMode": "replace" + } + }, "jsonValidation": [ { "fileMatch": "*.html-data.json", @@ -187,8 +203,8 @@ }, "dependencies": { "vscode-extension-telemetry": "0.1.1", - "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" diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index 2cb7e3d16973a..90e4e73f56826 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -24,5 +24,7 @@ "html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.", "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", "html.validate.styles": "Controls whether the built-in HTML language support validates embedded styles.", - "html.autoClosingTags": "Enable/disable autoclosing of HTML tags." + "html.autoClosingTags": "Enable/disable autoclosing of HTML tags.", + "html.mirrorCursorOnMatchingTag": "Enable/disable mirroring cursor on matching HTML tag.", + "html.mirrorCursorOnMatchingTagDeprecationMessage": "Deprecated in favor of `editor.renameOnType`" } diff --git a/extensions/html-language-features/schemas/package.schema.json b/extensions/html-language-features/schemas/package.schema.json index aaf9588221e41..a11810ef0909b 100644 --- a/extensions/html-language-features/schemas/package.schema.json +++ b/extensions/html-language-features/schemas/package.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "title": "HTML contributions to package.json", "type": "object", "properties": { diff --git a/extensions/html-language-features/server/build/javaScriptLibraryLoader.js b/extensions/html-language-features/server/build/javaScriptLibraryLoader.js new file mode 100644 index 0000000000000..85792138ba4ac --- /dev/null +++ b/extensions/html-language-features/server/build/javaScriptLibraryLoader.js @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// a webpack loader that bundles all library definitions (d.ts) for the embedded JavaScript engine. + +const path = require('path'); +const fs = require('fs'); + +const TYPESCRIPT_LIB_SOURCE = path.join(__dirname, '../../../node_modules/typescript/lib'); +const JQUERY_DTS = path.join(__dirname, '../lib/jquery.d.ts'); + +module.exports = function () { + function getFileName(name) { + return (name === '' ? 'lib.d.ts' : `lib.${name}.d.ts`); + } + function readLibFile(name) { + var srcPath = path.join(TYPESCRIPT_LIB_SOURCE, getFileName(name)); + return fs.readFileSync(srcPath).toString(); + } + + var queue = []; + var in_queue = {}; + + var enqueue = function (name) { + if (in_queue[name]) { + return; + } + in_queue[name] = true; + queue.push(name); + }; + + enqueue('es6'); + + var result = []; + while (queue.length > 0) { + var name = queue.shift(); + var contents = readLibFile(name); + var lines = contents.split(/\r\n|\r|\n/); + + var outputLines = []; + for (let i = 0; i < lines.length; i++) { + let m = lines[i].match(/\/\/\/\s*= 0; i--) { + strResult += `"${result[i].name}": ${result[i].output},\n`; + } + strResult += `\n};` + + strResult += `export function loadLibrary(name: string) : string {\n return libs[name] || ''; \n}`; + + return strResult; +} + +/** + * Escape text such that it can be used in a javascript string enclosed by double quotes (") + */ +function escapeText(text) { + // See http://www.javascriptkit.com/jsref/escapesequence.shtml + var _backspace = '\b'.charCodeAt(0); + var _formFeed = '\f'.charCodeAt(0); + var _newLine = '\n'.charCodeAt(0); + var _nullChar = 0; + var _carriageReturn = '\r'.charCodeAt(0); + var _tab = '\t'.charCodeAt(0); + var _verticalTab = '\v'.charCodeAt(0); + var _backslash = '\\'.charCodeAt(0); + var _doubleQuote = '"'.charCodeAt(0); + + var startPos = 0, chrCode, replaceWith = null, resultPieces = []; + + for (var i = 0, len = text.length; i < len; i++) { + chrCode = text.charCodeAt(i); + switch (chrCode) { + case _backspace: + replaceWith = '\\b'; + break; + case _formFeed: + replaceWith = '\\f'; + break; + case _newLine: + replaceWith = '\\n'; + break; + case _nullChar: + replaceWith = '\\0'; + break; + case _carriageReturn: + replaceWith = '\\r'; + break; + case _tab: + replaceWith = '\\t'; + break; + case _verticalTab: + replaceWith = '\\v'; + break; + case _backslash: + replaceWith = '\\\\'; + break; + case _doubleQuote: + replaceWith = '\\"'; + break; + } + if (replaceWith !== null) { + resultPieces.push(text.substring(startPos, i)); + resultPieces.push(replaceWith); + startPos = i + 1; + replaceWith = null; + } + } + resultPieces.push(text.substring(startPos, len)); + return resultPieces.join(''); +} diff --git a/extensions/html-language-features/server/extension-browser.webpack.config.js b/extensions/html-language-features/server/extension-browser.webpack.config.js new file mode 100644 index 0000000000000..ae024e8d7dd9b --- /dev/null +++ b/extensions/html-language-features/server/extension-browser.webpack.config.js @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * 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'); + +const serverConfig = withBrowserDefaults({ + context: __dirname, + entry: { + extension: './src/browser/htmlServerMain.ts', + }, + output: { + filename: 'htmlServerMain.js', + path: path.join(__dirname, 'dist', 'browser'), + libraryTarget: 'var' + }, + optimization: { + splitChunks: { + chunks: 'async' + } + } +}); +serverConfig.module.noParse = /typescript[\/\\]lib[\/\\]typescript\.js/; +serverConfig.module.rules.push({ + test: /javascriptLibs.ts$/, + use: [ + { + loader: path.resolve(__dirname, 'build', 'javaScriptLibraryLoader.js') + } + ] +}); + +module.exports = serverConfig; diff --git a/extensions/html-language-features/server/extension.webpack.config.js b/extensions/html-language-features/server/extension.webpack.config.js index 77b86e718b10c..33cb0b4f0a11f 100644 --- a/extensions/html-language-features/server/extension.webpack.config.js +++ b/extensions/html-language-features/server/extension.webpack.config.js @@ -13,11 +13,11 @@ const path = require('path'); module.exports = withDefaults({ context: path.join(__dirname), entry: { - extension: './src/htmlServerMain.ts', + extension: './src/node/htmlServerMain.ts', }, output: { filename: 'htmlServerMain.js', - path: path.join(__dirname, 'dist'), + path: path.join(__dirname, 'dist', 'node'), }, externals: { 'typescript': 'commonjs typescript' diff --git a/extensions/html-language-features/server/lib/jquery.d.ts b/extensions/html-language-features/server/lib/jquery.d.ts index 7ce90f7b80561..d31bfe6c1b546 100644 --- a/extensions/html-language-features/server/lib/jquery.d.ts +++ b/extensions/html-language-features/server/lib/jquery.d.ts @@ -34,7 +34,7 @@ interface JQueryAjaxSettings { /** * A pre-request callback function that can be used to modify the jqXHR (in jQuery 1.4.x, XMLHTTPRequest) object before it is sent. Use this to set custom headers, etc. The jqXHR and settings objects are passed as arguments. This is an Ajax Event. Returning false in the beforeSend function will cancel the request. As of jQuery 1.5, the beforeSend option will be called regardless of the type of request. */ - beforeSend? (jqXHR: JQueryXHR, settings: JQueryAjaxSettings): any; + beforeSend?(jqXHR: JQueryXHR, settings: JQueryAjaxSettings): any; /** * If set to false, it will force requested pages not to be cached by the browser. Note: Setting cache to false will only work correctly with HEAD and GET requests. It works by appending "_={timestamp}" to the GET parameters. The parameter is not needed for other types of requests, except in IE8 when a POST is made to a URL that has already been requested by a GET. */ @@ -42,7 +42,7 @@ interface JQueryAjaxSettings { /** * A function to be called when the request finishes (after success and error callbacks are executed). The function gets passed two arguments: The jqXHR (in jQuery 1.4.x, XMLHTTPRequest) object and a string categorizing the status of the request ("success", "notmodified", "error", "timeout", "abort", or "parsererror"). As of jQuery 1.5, the complete setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event. */ - complete? (jqXHR: JQueryXHR, textStatus: string): any; + complete?(jqXHR: JQueryXHR, textStatus: string): any; /** * An object of string/regular-expression pairs that determine how jQuery will parse the response, given its content type. (version added: 1.5) */ @@ -72,15 +72,15 @@ interface JQueryAjaxSettings { /** * A function to be used to handle the raw response data of XMLHttpRequest.This is a pre-filtering function to sanitize the response. You should return the sanitized data. The function accepts two arguments: The raw data returned from the server and the 'dataType' parameter. */ - dataFilter? (data: any, ty: any): any; + dataFilter?(data: any, ty: any): any; /** - * The type of data that you're expecting back from the server. If none is specified, jQuery will try to infer it based on the MIME type of the response (an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string). + * The type of data that you're expecting back from the server. If none is specified, jQuery will try to infer it based on the MIME type of the response (an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string). */ dataType?: string; /** * A function to be called if the request fails. The function receives three arguments: The jqXHR (in jQuery 1.4.x, XMLHttpRequest) object, a string describing the type of error that occurred and an optional exception object, if one occurred. Possible values for the second argument (besides null) are "timeout", "error", "abort", and "parsererror". When an HTTP error occurs, errorThrown receives the textual portion of the HTTP status, such as "Not Found" or "Internal Server Error." As of jQuery 1.5, the error setting can accept an array of functions. Each function will be called in turn. Note: This handler is not called for cross-domain script and cross-domain JSONP requests. This is an Ajax Event. */ - error? (jqXHR: JQueryXHR, textStatus: string, errorThrown: string): any; + error?(jqXHR: JQueryXHR, textStatus: string, errorThrown: string): any; /** * Whether to trigger global Ajax event handlers for this request. The default is true. Set to false to prevent the global handlers like ajaxStart or ajaxStop from being triggered. This can be used to control various Ajax Events. */ @@ -132,7 +132,7 @@ interface JQueryAjaxSettings { /** * A function to be called if the request succeeds. The function gets passed three arguments: The data returned from the server, formatted according to the dataType parameter; a string describing the status; and the jqXHR (in jQuery 1.4.x, XMLHttpRequest) object. As of jQuery 1.5, the success setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event. */ - success? (data: any, textStatus: string, jqXHR: JQueryXHR): any; + success?(data: any, textStatus: string, jqXHR: JQueryXHR): any; /** * Set a timeout (in milliseconds) for the request. This will override any global timeout set with $.ajaxSetup(). The timeout period starts at the point the $.ajax call is made; if several other requests are in progress and the browser has no connections available, it is possible for a request to time out before it can be sent. In jQuery 1.4.x and below, the XMLHttpRequest object will be in an invalid state if the request times out; accessing any object members may throw an exception. In Firefox 3.0+ only, script and JSONP requests cannot be cancelled by a timeout; the script will run even if it arrives after the timeout period. */ @@ -168,11 +168,11 @@ interface JQueryAjaxSettings { */ interface JQueryXHR extends XMLHttpRequest, JQueryPromise { /** - * The .overrideMimeType() method may be used in the beforeSend() callback function, for example, to modify the response content-type header. As of jQuery 1.5.1, the jqXHR object also contains the overrideMimeType() method (it was available in jQuery 1.4.x, as well, but was temporarily removed in jQuery 1.5). + * The .overrideMimeType() method may be used in the beforeSend() callback function, for example, to modify the response content-type header. As of jQuery 1.5.1, the jqXHR object also contains the overrideMimeType() method (it was available in jQuery 1.4.x, as well, but was temporarily removed in jQuery 1.5). */ overrideMimeType(mimeType: string): any; /** - * Cancel the request. + * Cancel the request. * * @param statusText A string passed as the textStatus parameter for the done callback. Default value: "canceled" */ @@ -197,13 +197,13 @@ interface JQueryXHR extends XMLHttpRequest, JQueryPromise { interface JQueryCallback { /** * Add a callback or a collection of callbacks to a callback list. - * + * * @param callbacks A function, or array of functions, that are to be added to the callback list. */ add(callbacks: Function): JQueryCallback; /** * Add a callback or a collection of callbacks to a callback list. - * + * * @param callbacks A function, or array of functions, that are to be added to the callback list. */ add(callbacks: Function[]): JQueryCallback; @@ -225,7 +225,7 @@ interface JQueryCallback { /** * Call all of the callbacks with the given arguments - * + * * @param arguments The argument or list of arguments to pass back to the callback list. */ fire(...arguments: any[]): JQueryCallback; @@ -237,7 +237,7 @@ interface JQueryCallback { /** * Call all callbacks in a list with the given context and arguments. - * + * * @param context A reference to the context in which the callbacks in the list should be fired. * @param arguments An argument, or array of arguments, to pass to the callbacks in the list. */ @@ -245,7 +245,7 @@ interface JQueryCallback { /** * Determine whether a supplied callback is in a list - * + * * @param callback The callback to search for. */ has(callback: Function): boolean; @@ -262,13 +262,13 @@ interface JQueryCallback { /** * Remove a callback or a collection of callbacks from a callback list. - * + * * @param callbacks A function, or array of functions, that are to be removed from the callback list. */ remove(callbacks: Function): JQueryCallback; /** * Remove a callback or a collection of callbacks from a callback list. - * + * * @param callbacks A function, or array of functions, that are to be removed from the callback list. */ remove(callbacks: Function[]): JQueryCallback; @@ -280,15 +280,15 @@ interface JQueryCallback { interface JQueryGenericPromise { /** * Add handlers to be called when the Deferred object is resolved, rejected, or still in progress. - * + * * @param doneFilter A function that is called when the Deferred is resolved. * @param failFilter An optional function that is called when the Deferred is rejected. */ - then(doneFilter: (value?: T, ...values: any[]) => U|JQueryPromise, failFilter?: (...reasons: any[]) => any, progressFilter?: (...progression: any[]) => any): JQueryPromise; + then(doneFilter: (value?: T, ...values: any[]) => U | JQueryPromise, failFilter?: (...reasons: any[]) => any, progressFilter?: (...progression: any[]) => any): JQueryPromise; /** * Add handlers to be called when the Deferred object is resolved, rejected, or still in progress. - * + * * @param doneFilter A function that is called when the Deferred is resolved. * @param failFilter An optional function that is called when the Deferred is rejected. */ @@ -303,7 +303,7 @@ interface JQueryPromiseCallback { } interface JQueryPromiseOperator { - (callback1: JQueryPromiseCallback|JQueryPromiseCallback[], ...callbacksN: Array|JQueryPromiseCallback[]>): JQueryPromise; + (callback1: JQueryPromiseCallback | JQueryPromiseCallback[], ...callbacksN: Array | JQueryPromiseCallback[]>): JQueryPromise; } /** @@ -316,38 +316,38 @@ interface JQueryPromise extends JQueryGenericPromise { state(): string; /** * Add handlers to be called when the Deferred object is either resolved or rejected. - * + * * @param alwaysCallbacks1 A function, or array of functions, that is called when the Deferred is resolved or rejected. * @param alwaysCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is resolved or rejected. */ - always(alwaysCallback1?: JQueryPromiseCallback|JQueryPromiseCallback[], ...alwaysCallbacksN: Array|JQueryPromiseCallback[]>): JQueryPromise; + always(alwaysCallback1?: JQueryPromiseCallback | JQueryPromiseCallback[], ...alwaysCallbacksN: Array | JQueryPromiseCallback[]>): JQueryPromise; /** * Add handlers to be called when the Deferred object is resolved. - * + * * @param doneCallbacks1 A function, or array of functions, that are called when the Deferred is resolved. * @param doneCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is resolved. */ - done(doneCallback1?: JQueryPromiseCallback|JQueryPromiseCallback[], ...doneCallbackN: Array|JQueryPromiseCallback[]>): JQueryPromise; + done(doneCallback1?: JQueryPromiseCallback | JQueryPromiseCallback[], ...doneCallbackN: Array | JQueryPromiseCallback[]>): JQueryPromise; /** * Add handlers to be called when the Deferred object is rejected. - * + * * @param failCallbacks1 A function, or array of functions, that are called when the Deferred is rejected. * @param failCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is rejected. */ - fail(failCallback1?: JQueryPromiseCallback|JQueryPromiseCallback[], ...failCallbacksN: Array|JQueryPromiseCallback[]>): JQueryPromise; + fail(failCallback1?: JQueryPromiseCallback | JQueryPromiseCallback[], ...failCallbacksN: Array | JQueryPromiseCallback[]>): JQueryPromise; /** * Add handlers to be called when the Deferred object generates progress notifications. - * + * * @param progressCallbacks A function, or array of functions, to be called when the Deferred generates progress notifications. */ - progress(progressCallback1?: JQueryPromiseCallback|JQueryPromiseCallback[], ...progressCallbackN: Array|JQueryPromiseCallback[]>): JQueryPromise; + progress(progressCallback1?: JQueryPromiseCallback | JQueryPromiseCallback[], ...progressCallbackN: Array | JQueryPromiseCallback[]>): JQueryPromise; // Deprecated - given no typings pipe(doneFilter?: (x: any) => any, failFilter?: (x: any) => any, progressFilter?: (x: any) => any): JQueryPromise; - + /** * Return a Deferred's Promise object. - * + * * @param target Object onto which the promise methods have to be attached */ promise(target?: any): JQueryPromise; @@ -363,42 +363,42 @@ interface JQueryDeferred extends JQueryGenericPromise { state(): string; /** * Add handlers to be called when the Deferred object is either resolved or rejected. - * + * * @param alwaysCallbacks1 A function, or array of functions, that is called when the Deferred is resolved or rejected. * @param alwaysCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is resolved or rejected. */ - always(alwaysCallback1?: JQueryPromiseCallback|JQueryPromiseCallback[], ...alwaysCallbacksN: Array|JQueryPromiseCallback[]>): JQueryDeferred; + always(alwaysCallback1?: JQueryPromiseCallback | JQueryPromiseCallback[], ...alwaysCallbacksN: Array | JQueryPromiseCallback[]>): JQueryDeferred; /** * Add handlers to be called when the Deferred object is resolved. - * + * * @param doneCallbacks1 A function, or array of functions, that are called when the Deferred is resolved. * @param doneCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is resolved. */ - done(doneCallback1?: JQueryPromiseCallback|JQueryPromiseCallback[], ...doneCallbackN: Array|JQueryPromiseCallback[]>): JQueryDeferred; + done(doneCallback1?: JQueryPromiseCallback | JQueryPromiseCallback[], ...doneCallbackN: Array | JQueryPromiseCallback[]>): JQueryDeferred; /** * Add handlers to be called when the Deferred object is rejected. - * + * * @param failCallbacks1 A function, or array of functions, that are called when the Deferred is rejected. * @param failCallbacks2 Optional additional functions, or arrays of functions, that are called when the Deferred is rejected. */ - fail(failCallback1?: JQueryPromiseCallback|JQueryPromiseCallback[], ...failCallbacksN: Array|JQueryPromiseCallback[]>): JQueryDeferred; + fail(failCallback1?: JQueryPromiseCallback | JQueryPromiseCallback[], ...failCallbacksN: Array | JQueryPromiseCallback[]>): JQueryDeferred; /** * Add handlers to be called when the Deferred object generates progress notifications. - * + * * @param progressCallbacks A function, or array of functions, to be called when the Deferred generates progress notifications. */ - progress(progressCallback1?: JQueryPromiseCallback|JQueryPromiseCallback[], ...progressCallbackN: Array|JQueryPromiseCallback[]>): JQueryDeferred; + progress(progressCallback1?: JQueryPromiseCallback | JQueryPromiseCallback[], ...progressCallbackN: Array | JQueryPromiseCallback[]>): JQueryDeferred; /** * Call the progressCallbacks on a Deferred object with the given args. - * + * * @param args Optional arguments that are passed to the progressCallbacks. */ notify(value?: any, ...args: any[]): JQueryDeferred; /** * Call the progressCallbacks on a Deferred object with the given context and args. - * + * * @param context Context passed to the progressCallbacks as the this object. * @param args Optional arguments that are passed to the progressCallbacks. */ @@ -406,13 +406,13 @@ interface JQueryDeferred extends JQueryGenericPromise { /** * Reject a Deferred object and call any failCallbacks with the given args. - * + * * @param args Optional arguments that are passed to the failCallbacks. */ reject(value?: any, ...args: any[]): JQueryDeferred; /** * Reject a Deferred object and call any failCallbacks with the given context and args. - * + * * @param context Context passed to the failCallbacks as the this object. * @param args An optional array of arguments that are passed to the failCallbacks. */ @@ -420,7 +420,7 @@ interface JQueryDeferred extends JQueryGenericPromise { /** * Resolve a Deferred object and call any doneCallbacks with the given args. - * + * * @param value First argument passed to doneCallbacks. * @param args Optional subsequent arguments that are passed to the doneCallbacks. */ @@ -428,7 +428,7 @@ interface JQueryDeferred extends JQueryGenericPromise { /** * Resolve a Deferred object and call any doneCallbacks with the given context and args. - * + * * @param context Context passed to the doneCallbacks as the this object. * @param args An optional array of arguments that are passed to the doneCallbacks. */ @@ -436,7 +436,7 @@ interface JQueryDeferred extends JQueryGenericPromise { /** * Return a Deferred's Promise object. - * + * * @param target Object onto which the promise methods have to be attached */ promise(target?: any): JQueryPromise; @@ -495,7 +495,7 @@ interface JQueryKeyEventObject extends JQueryInputEventObject { keyCode: number; } -interface JQueryEventObject extends BaseJQueryEventObject, JQueryInputEventObject, JQueryMouseEventObject, JQueryKeyEventObject{ +interface JQueryEventObject extends BaseJQueryEventObject, JQueryInputEventObject, JQueryMouseEventObject, JQueryKeyEventObject { } /* @@ -518,7 +518,7 @@ interface JQuerySupport { opacity?: boolean; optDisabled?: boolean; optSelected?: boolean; - scriptEval? (): boolean; + scriptEval?(): boolean; style?: boolean; submitBubbles?: boolean; tbody?: boolean; @@ -527,14 +527,14 @@ interface JQuerySupport { interface JQueryParam { /** * Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request. - * + * * @param obj An array or object to serialize. */ (obj: any): string; /** * Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request. - * + * * @param obj An array or object to serialize. * @param traditional A Boolean indicating whether to perform a traditional "shallow" serialization. */ @@ -549,7 +549,7 @@ interface JQueryParam { */ interface JQueryEventConstructor { (name: string, eventProperties?: any): JQueryEventObject; - new (name: string, eventProperties?: any): JQueryEventObject; + new(name: string, eventProperties?: any): JQueryEventObject; } /** @@ -568,47 +568,47 @@ interface JQuerySerializeArrayElement { value: string; } -interface JQueryAnimationOptions { +interface JQueryAnimationOptions { /** * A string or number determining how long the animation will run. */ - duration?: any; + duration?: any; /** * A string indicating which easing function to use for the transition. */ - easing?: string; + easing?: string; /** * A function to call once the animation is complete. */ - complete?: Function; + complete?: Function; /** * A function to be called for each animated property of each animated element. This function provides an opportunity to modify the Tween object to change the value of the property before it is set. */ - step?: (now: number, tween: any) => any; + step?: (now: number, tween: any) => any; /** * A function to be called after each step of the animation, only once per animated element regardless of the number of animated properties. (version added: 1.8) */ - progress?: (animation: JQueryPromise, progress: number, remainingMs: number) => any; + progress?: (animation: JQueryPromise, progress: number, remainingMs: number) => any; /** * A function to call when the animation begins. (version added: 1.8) */ - start?: (animation: JQueryPromise) => any; + start?: (animation: JQueryPromise) => any; /** * A function to be called when the animation completes (its Promise object is resolved). (version added: 1.8) */ - done?: (animation: JQueryPromise, jumpedToEnd: boolean) => any; + done?: (animation: JQueryPromise, jumpedToEnd: boolean) => any; /** * A function to be called when the animation fails to complete (its Promise object is rejected). (version added: 1.8) */ - fail?: (animation: JQueryPromise, jumpedToEnd: boolean) => any; + fail?: (animation: JQueryPromise, jumpedToEnd: boolean) => any; /** * A function to be called when the animation completes or stops without completing (its Promise object is either resolved or rejected). (version added: 1.8) */ - always?: (animation: JQueryPromise, jumpedToEnd: boolean) => any; + always?: (animation: JQueryPromise, jumpedToEnd: boolean) => any; /** * A Boolean indicating whether to place the animation in the effects queue. If false, the animation will begin immediately. As of jQuery 1.7, the queue option can also accept a string, in which case the animation is added to the queue represented by that string. When a custom queue name is used the animation does not automatically start; you must call .dequeue("queuename") to start it. */ - queue?: any; + queue?: any; /** * A map of one or more of the CSS properties defined by the properties argument and their corresponding easing functions. (version added: 1.4) */ @@ -616,11 +616,11 @@ interface JQueryAnimationOptions { } interface JQueryEasingFunction { - ( percent: number ): number; + (percent: number): number; } interface JQueryEasingFunctions { - [ name: string ]: JQueryEasingFunction; + [name: string]: JQueryEasingFunction; linear: JQueryEasingFunction; swing: JQueryEasingFunction; } @@ -660,11 +660,11 @@ interface JQueryStatic { ajaxSettings: JQueryAjaxSettings; - /** - * Set default values for future Ajax requests. Its use is not recommended. - * - * @param options A set of key/value pairs that configure the default Ajax request. All options are optional. - */ + /** + * Set default values for future Ajax requests. Its use is not recommended. + * + * @param options A set of key/value pairs that configure the default Ajax request. All options are optional. + */ ajaxSetup(options: JQueryAjaxSettings): void; /** @@ -683,13 +683,13 @@ interface JQueryStatic { * @param success A callback function that is executed if the request succeeds. * @param dataType The type of data expected from the server. Default: Intelligent Guess (xml, json, script, or html). */ - get(url: string, data?: Object|string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any, dataType?: string): JQueryXHR; + get(url: string, data?: Object | string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any, dataType?: string): JQueryXHR; /** * Load data from the server using a HTTP GET request. * * @param settings The JQueryAjaxSettings to be used for the request */ - get(settings : JQueryAjaxSettings): JQueryXHR; + get(settings: JQueryAjaxSettings): JQueryXHR; /** * Load JSON-encoded data from the server using a GET HTTP request. * @@ -704,7 +704,7 @@ interface JQueryStatic { * @param data A plain object or string that is sent to the server with the request. * @param success A callback function that is executed if the request succeeds. */ - getJSON(url: string, data?: Object|string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any): JQueryXHR; + getJSON(url: string, data?: Object | string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any): JQueryXHR; /** * Load a JavaScript file from the server using a GET HTTP request, then execute it. * @@ -734,13 +734,13 @@ interface JQueryStatic { * @param success A callback function that is executed if the request succeeds. Required if dataType is provided, but can be null in that case. * @param dataType The type of data expected from the server. Default: Intelligent Guess (xml, json, script, text, html). */ - post(url: string, data?: Object|string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any, dataType?: string): JQueryXHR; + post(url: string, data?: Object | string, success?: (data: any, textStatus: string, jqXHR: JQueryXHR) => any, dataType?: string): JQueryXHR; /** * Load data from the server using a HTTP POST request. * * @param settings The JQueryAjaxSettings to be used for the request */ - post(settings : JQueryAjaxSettings): JQueryXHR; + post(settings: JQueryAjaxSettings): JQueryXHR; /** * A multi-purpose callbacks list object that provides a powerful way to manage callback lists. * @@ -761,7 +761,7 @@ interface JQueryStatic { * @param selector A string containing a selector expression * @param context A DOM Element, Document, or jQuery to use as context */ - (selector: string, context?: Element|JQuery): JQuery; + (selector: string, context?: Element | JQuery): JQuery; /** * Accepts a string containing a CSS selector which is then used to match a set of elements. @@ -831,7 +831,7 @@ interface JQueryStatic { * * @param deferreds One or more Deferred objects, or plain JavaScript objects. */ - when(...deferreds: Array/* as JQueryDeferred */>): JQueryPromise; + when(...deferreds: Array/* as JQueryDeferred */>): JQueryPromise; /** * Hook directly into jQuery to override how particular CSS properties are retrieved or set, normalize CSS property naming, or create custom properties. @@ -972,7 +972,7 @@ interface JQueryStatic { /** * Check to see if a DOM element is a descendant of another DOM element. - * + * * @param container The DOM element that may contain the other element. * @param contained The DOM element that may be contained by (a descendant of) the other element. */ @@ -980,25 +980,25 @@ interface JQueryStatic { /** * A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties. - * + * * @param collection The object or array to iterate over. * @param callback The function that will be executed on every object. */ each( collection: T[], callback: (indexInArray: number, valueOfElement: T) => any - ): any; + ): any; /** * A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties. - * + * * @param collection The object or array to iterate over. * @param callback The function that will be executed on every object. */ each( collection: any, callback: (indexInArray: any, valueOfElement: any) => any - ): any; + ): any; /** * Merge the contents of two or more objects together into the first object. @@ -1088,21 +1088,21 @@ interface JQueryStatic { /** * Convert an array-like object into a true JavaScript array. - * + * * @param obj Any object to turn into a native Array. */ makeArray(obj: any): any[]; /** * Translate all items in an array or object to new array of items. - * + * * @param array The Array to translate. * @param callback The function to process each item against. The first argument to the function is the array item, the second argument is the index in array The function can return any value. Within the function, this refers to the global (window) object. */ map(array: T[], callback: (elementOfArray?: T, indexInArray?: number) => U): U[]; /** * Translate all items in an array or object to new array of items. - * + * * @param arrayOrObject The Array or Object to translate. * @param callback The function to process each item against. The first argument to the function is the value; the second argument is the index or key of the array or object property. The function can return any value to add to the array. A returned array will be flattened into the resulting array. Within the function, this refers to the global (window) object. */ @@ -1110,7 +1110,7 @@ interface JQueryStatic { /** * Merge the contents of two arrays together into the first array. - * + * * @param first The first array to merge, the elements of second added. * @param second The second array to merge into the first, unaltered. */ @@ -1128,7 +1128,7 @@ interface JQueryStatic { /** * Takes a well-formed JSON string and returns the resulting JavaScript object. - * + * * @param json The JSON string to parse. */ parseJSON(json: string): any; @@ -1142,21 +1142,21 @@ interface JQueryStatic { /** * Remove the whitespace from the beginning and end of a string. - * + * * @param str Remove the whitespace from the beginning and end of a string. */ trim(str: string): string; /** * Determine the internal JavaScript [[Class]] of an object. - * + * * @param obj Object to get the internal JavaScript [[Class]] of. */ type(obj: any): string; /** * Sorts an array of DOM elements, in place, with the duplicates removed. Note that this only works on arrays of DOM elements, not strings or numbers. - * + * * @param array The Array of DOM elements. */ unique(array: Element[]): Element[]; @@ -1228,7 +1228,7 @@ interface JQuery { * @param data A plain object or string that is sent to the server with the request. * @param complete A callback function that is executed when the request completes. */ - load(url: string, data?: string|Object, complete?: (responseText: string, textStatus: string, XMLHttpRequest: XMLHttpRequest) => any): JQuery; + load(url: string, data?: string | Object, complete?: (responseText: string, textStatus: string, XMLHttpRequest: XMLHttpRequest) => any): JQuery; /** * Encode a set of form elements as a string for submission. @@ -1269,21 +1269,21 @@ interface JQuery { * @param attributeName The name of the attribute to set. * @param value A value to set for the attribute. */ - attr(attributeName: string, value: string|number): JQuery; + attr(attributeName: string, value: string | number): JQuery; /** * Set one or more attributes for the set of matched elements. * * @param attributeName The name of the attribute to set. * @param func A function returning the value to set. this is the current element. Receives the index position of the element in the set and the old attribute value as arguments. */ - attr(attributeName: string, func: (index: number, attr: string) => string|number): JQuery; + attr(attributeName: string, func: (index: number, attr: string) => string | number): JQuery; /** * Set one or more attributes for the set of matched elements. * * @param attributes An object of attribute-value pairs to set. */ attr(attributes: Object): JQuery; - + /** * Determine whether any of the matched elements are assigned the given class. * @@ -1325,7 +1325,7 @@ interface JQuery { * @param propertyName The name of the property to set. * @param value A value to set for the property. */ - prop(propertyName: string, value: string|number|boolean): JQuery; + prop(propertyName: string, value: string | number | boolean): JQuery; /** * Set one or more properties for the set of matched elements. * @@ -1397,7 +1397,7 @@ interface JQuery { * * @param value A string of text, an array of strings or number corresponding to the value of each matched element to set as selected/checked. */ - val(value: string|string[]|number): JQuery; + val(value: string | string[] | number): JQuery; /** * Set the value of each element in the set of matched elements. * @@ -1418,14 +1418,14 @@ interface JQuery { * @param propertyName A CSS property name. * @param value A value to set for the property. */ - css(propertyName: string, value: string|number): JQuery; + css(propertyName: string, value: string | number): JQuery; /** * Set one or more CSS properties for the set of matched elements. * * @param propertyName A CSS property name. * @param value A function returning the value to set. this is the current element. Receives the index position of the element in the set and the old value as arguments. */ - css(propertyName: string, value: (index: number, value: string) => string|number): JQuery; + css(propertyName: string, value: (index: number, value: string) => string | number): JQuery; /** * Set one or more CSS properties for the set of matched elements. * @@ -1442,13 +1442,13 @@ interface JQuery { * * @param value An integer representing the number of pixels, or an integer with an optional unit of measure appended (as a string). */ - height(value: number|string): JQuery; + height(value: number | string): JQuery; /** * Set the CSS height of every matched element. * * @param func A function returning the height to set. Receives the index position of the element in the set and the old height as arguments. Within the function, this refers to the current element in the set. */ - height(func: (index: number, height: number) => number|string): JQuery; + height(func: (index: number, height: number) => number | string): JQuery; /** * Get the current computed height for the first element in the set of matched elements, including padding but not border. @@ -1460,8 +1460,8 @@ interface JQuery { * * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string). */ - innerHeight(height: number|string): JQuery; - + innerHeight(height: number | string): JQuery; + /** * Get the current computed width for the first element in the set of matched elements, including padding but not border. */ @@ -1472,8 +1472,8 @@ interface JQuery { * * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string). */ - innerWidth(width: number|string): JQuery; - + innerWidth(width: number | string): JQuery; + /** * Get the current coordinates of the first element in the set of matched elements, relative to the document. */ @@ -1503,7 +1503,7 @@ interface JQuery { * * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string). */ - outerHeight(height: number|string): JQuery; + outerHeight(height: number | string): JQuery; /** * Get the current computed width for the first element in the set of matched elements, including padding and border. @@ -1517,7 +1517,7 @@ interface JQuery { * * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string). */ - outerWidth(width: number|string): JQuery; + outerWidth(width: number | string): JQuery; /** * Get the current coordinates of the first element in the set of matched elements, relative to the offset parent. @@ -1555,13 +1555,13 @@ interface JQuery { * * @param value An integer representing the number of pixels, or an integer along with an optional unit of measure appended (as a string). */ - width(value: number|string): JQuery; + width(value: number | string): JQuery; /** * Set the CSS width of each element in the set of matched elements. * * @param func A function returning the width to set. Receives the index position of the element in the set and the old width as arguments. Within the function, this refers to the current element in the set. */ - width(func: (index: number, width: number) => number|string): JQuery; + width(func: (index: number, width: number) => number | string): JQuery; /** * Remove from the queue all items that have not yet been run. @@ -1633,7 +1633,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - animate(properties: Object, duration?: string|number, complete?: Function): JQuery; + animate(properties: Object, duration?: string | number, complete?: Function): JQuery; /** * Perform a custom animation of a set of CSS properties. * @@ -1642,7 +1642,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. (default: swing) * @param complete A function to call once the animation is complete. */ - animate(properties: Object, duration?: string|number, easing?: string, complete?: Function): JQuery; + animate(properties: Object, duration?: string | number, easing?: string, complete?: Function): JQuery; /** * Perform a custom animation of a set of CSS properties. * @@ -1665,7 +1665,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - fadeIn(duration?: number|string, complete?: Function): JQuery; + fadeIn(duration?: number | string, complete?: Function): JQuery; /** * Display the matched elements by fading them to opaque. * @@ -1673,7 +1673,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - fadeIn(duration?: number|string, easing?: string, complete?: Function): JQuery; + fadeIn(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Display the matched elements by fading them to opaque. * @@ -1687,7 +1687,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - fadeOut(duration?: number|string, complete?: Function): JQuery; + fadeOut(duration?: number | string, complete?: Function): JQuery; /** * Hide the matched elements by fading them to transparent. * @@ -1695,7 +1695,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - fadeOut(duration?: number|string, easing?: string, complete?: Function): JQuery; + fadeOut(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Hide the matched elements by fading them to transparent. * @@ -1710,7 +1710,7 @@ interface JQuery { * @param opacity A number between 0 and 1 denoting the target opacity. * @param complete A function to call once the animation is complete. */ - fadeTo(duration: string|number, opacity: number, complete?: Function): JQuery; + fadeTo(duration: string | number, opacity: number, complete?: Function): JQuery; /** * Adjust the opacity of the matched elements. * @@ -1719,7 +1719,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - fadeTo(duration: string|number, opacity: number, easing?: string, complete?: Function): JQuery; + fadeTo(duration: string | number, opacity: number, easing?: string, complete?: Function): JQuery; /** * Display or hide the matched elements by animating their opacity. @@ -1727,7 +1727,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - fadeToggle(duration?: number|string, complete?: Function): JQuery; + fadeToggle(duration?: number | string, complete?: Function): JQuery; /** * Display or hide the matched elements by animating their opacity. * @@ -1735,7 +1735,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - fadeToggle(duration?: number|string, easing?: string, complete?: Function): JQuery; + fadeToggle(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Display or hide the matched elements by animating their opacity. * @@ -1756,7 +1756,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - hide(duration?: number|string, complete?: Function): JQuery; + hide(duration?: number | string, complete?: Function): JQuery; /** * Hide the matched elements. * @@ -1764,7 +1764,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - hide(duration?: number|string, easing?: string, complete?: Function): JQuery; + hide(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Hide the matched elements. * @@ -1778,7 +1778,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - show(duration?: number|string, complete?: Function): JQuery; + show(duration?: number | string, complete?: Function): JQuery; /** * Display the matched elements. * @@ -1786,7 +1786,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - show(duration?: number|string, easing?: string, complete?: Function): JQuery; + show(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Display the matched elements. * @@ -1800,7 +1800,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - slideDown(duration?: number|string, complete?: Function): JQuery; + slideDown(duration?: number | string, complete?: Function): JQuery; /** * Display the matched elements with a sliding motion. * @@ -1808,7 +1808,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - slideDown(duration?: number|string, easing?: string, complete?: Function): JQuery; + slideDown(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Display the matched elements with a sliding motion. * @@ -1822,7 +1822,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - slideToggle(duration?: number|string, complete?: Function): JQuery; + slideToggle(duration?: number | string, complete?: Function): JQuery; /** * Display or hide the matched elements with a sliding motion. * @@ -1830,7 +1830,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - slideToggle(duration?: number|string, easing?: string, complete?: Function): JQuery; + slideToggle(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Display or hide the matched elements with a sliding motion. * @@ -1844,7 +1844,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - slideUp(duration?: number|string, complete?: Function): JQuery; + slideUp(duration?: number | string, complete?: Function): JQuery; /** * Hide the matched elements with a sliding motion. * @@ -1852,7 +1852,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - slideUp(duration?: number|string, easing?: string, complete?: Function): JQuery; + slideUp(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Hide the matched elements with a sliding motion. * @@ -1882,7 +1882,7 @@ interface JQuery { * @param duration A string or number determining how long the animation will run. * @param complete A function to call once the animation is complete. */ - toggle(duration?: number|string, complete?: Function): JQuery; + toggle(duration?: number | string, complete?: Function): JQuery; /** * Display or hide the matched elements. * @@ -1890,7 +1890,7 @@ interface JQuery { * @param easing A string indicating which easing function to use for the transition. * @param complete A function to call once the animation is complete. */ - toggle(duration?: number|string, easing?: string, complete?: Function): JQuery; + toggle(duration?: number | string, easing?: string, complete?: Function): JQuery; /** * Display or hide the matched elements. * @@ -1906,7 +1906,7 @@ interface JQuery { /** * Attach a handler to an event for the elements. - * + * * @param eventType A string containing one or more DOM event types, such as "click" or "submit," or custom event names. * @param eventData An object containing data that will be passed to the event handler. * @param handler A function to execute each time the event is triggered. @@ -1914,14 +1914,14 @@ interface JQuery { bind(eventType: string, eventData: any, handler: (eventObject: JQueryEventObject) => any): JQuery; /** * Attach a handler to an event for the elements. - * + * * @param eventType A string containing one or more DOM event types, such as "click" or "submit," or custom event names. * @param handler A function to execute each time the event is triggered. */ bind(eventType: string, handler: (eventObject: JQueryEventObject) => any): JQuery; /** * Attach a handler to an event for the elements. - * + * * @param eventType A string containing one or more DOM event types, such as "click" or "submit," or custom event names. * @param eventData An object containing data that will be passed to the event handler. * @param preventBubble Setting the third argument to false will attach a function that prevents the default action from occurring and stops the event from bubbling. The default is true. @@ -1929,14 +1929,14 @@ interface JQuery { bind(eventType: string, eventData: any, preventBubble: boolean): JQuery; /** * Attach a handler to an event for the elements. - * + * * @param eventType A string containing one or more DOM event types, such as "click" or "submit," or custom event names. * @param preventBubble Setting the third argument to false will attach a function that prevents the default action from occurring and stops the event from bubbling. The default is true. */ bind(eventType: string, preventBubble: boolean): JQuery; /** * Attach a handler to an event for the elements. - * + * * @param events An object containing one or more DOM event types and functions to execute for them. */ bind(events: any): JQuery; @@ -2344,7 +2344,7 @@ interface JQuery { * @param data Data to be passed to the handler in event.data when an event is triggered. * @param handler A function to execute when the event is triggered. The value false is also allowed as a shorthand for a function that simply does return false. */ - on(events: string, data : any, handler: (eventObject: JQueryEventObject, ...args: any[]) => any): JQuery; + on(events: string, data: any, handler: (eventObject: JQueryEventObject, ...args: any[]) => any): JQuery; /** * Attach an event handler function for one or more events to the selected elements. * @@ -2511,22 +2511,22 @@ interface JQuery { /** * Execute all handlers and behaviors attached to the matched elements for the given event type. - * + * * @param eventType A string containing a JavaScript event type, such as click or submit. * @param extraParameters Additional parameters to pass along to the event handler. */ - trigger(eventType: string, extraParameters?: any[]|Object): JQuery; + trigger(eventType: string, extraParameters?: any[] | Object): JQuery; /** * Execute all handlers and behaviors attached to the matched elements for the given event type. - * + * * @param event A jQuery.Event object. * @param extraParameters Additional parameters to pass along to the event handler. */ - trigger(event: JQueryEventObject, extraParameters?: any[]|Object): JQuery; + trigger(event: JQueryEventObject, extraParameters?: any[] | Object): JQuery; /** * Execute all handlers attached to an element for an event. - * + * * @param eventType A string containing a JavaScript event type, such as click or submit. * @param extraParameters An array of additional parameters to pass along to the event handler. */ @@ -2534,7 +2534,7 @@ interface JQuery { /** * Execute all handlers attached to an element for an event. - * + * * @param event A jQuery.Event object. * @param extraParameters An array of additional parameters to pass along to the event handler. */ @@ -2542,21 +2542,21 @@ interface JQuery { /** * Remove a previously-attached event handler from the elements. - * + * * @param eventType A string containing a JavaScript event type, such as click or submit. * @param handler The function that is to be no longer executed. */ unbind(eventType?: string, handler?: (eventObject: JQueryEventObject) => any): JQuery; /** * Remove a previously-attached event handler from the elements. - * + * * @param eventType A string containing a JavaScript event type, such as click or submit. * @param fls Unbinds the corresponding 'return false' function that was bound using .bind( eventType, false ). */ unbind(eventType: string, fls: boolean): JQuery; /** * Remove a previously-attached event handler from the elements. - * + * * @param evt A JavaScript event object as passed to an event handler. */ unbind(evt: any): JQuery; @@ -2567,7 +2567,7 @@ interface JQuery { undelegate(): JQuery; /** * Remove a handler from the event for all elements which match the current selector, based upon a specific set of root elements. - * + * * @param selector A selector which will be used to filter the event results. * @param eventType A string containing a JavaScript event type, such as "click" or "keydown" * @param handler A function to execute at the time the event is triggered. @@ -2575,27 +2575,27 @@ interface JQuery { undelegate(selector: string, eventType: string, handler?: (eventObject: JQueryEventObject) => any): JQuery; /** * Remove a handler from the event for all elements which match the current selector, based upon a specific set of root elements. - * + * * @param selector A selector which will be used to filter the event results. * @param events An object of one or more event types and previously bound functions to unbind from them. */ undelegate(selector: string, events: Object): JQuery; /** * Remove a handler from the event for all elements which match the current selector, based upon a specific set of root elements. - * + * * @param namespace A string containing a namespace to unbind all events from. */ undelegate(namespace: string): JQuery; /** * Bind an event handler to the "unload" JavaScript event. (DEPRECATED from v1.8) - * + * * @param handler A function to execute when the event is triggered. */ unload(handler: (eventObject: JQueryEventObject) => any): JQuery; /** * Bind an event handler to the "unload" JavaScript event. (DEPRECATED from v1.8) - * + * * @param eventData A plain object of data that will be passed to the event handler. * @param handler A function to execute when the event is triggered. */ @@ -2610,13 +2610,13 @@ interface JQuery { /** * Bind an event handler to the "error" JavaScript event. (DEPRECATED from v1.8) - * + * * @param handler A function to execute when the event is triggered. */ error(handler: (eventObject: JQueryEventObject) => any): JQuery; /** * Bind an event handler to the "error" JavaScript event. (DEPRECATED from v1.8) - * + * * @param eventData A plain object of data that will be passed to the event handler. * @param handler A function to execute when the event is triggered. */ @@ -2624,13 +2624,13 @@ interface JQuery { /** * Add a collection of DOM elements onto the jQuery stack. - * + * * @param elements An array of elements to push onto the stack and make into a new jQuery object. */ pushStack(elements: any[]): JQuery; /** * Add a collection of DOM elements onto the jQuery stack. - * + * * @param elements An array of elements to push onto the stack and make into a new jQuery object. * @param name The name of a jQuery method that generated the array of elements. * @param arguments The arguments that were passed in to the jQuery method (for serialization). @@ -2639,56 +2639,56 @@ interface JQuery { /** * Insert content, specified by the parameter, after each element in the set of matched elements. - * + * * param content1 HTML string, DOM element, DocumentFragment, array of elements, or jQuery object to insert after each element in the set of matched elements. * param content2 One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert after each element in the set of matched elements. */ - after(content1: JQuery|any[]|Element|DocumentFragment|Text|string, ...content2: any[]): JQuery; + after(content1: JQuery | any[] | Element | DocumentFragment | Text | string, ...content2: any[]): JQuery; /** * Insert content, specified by the parameter, after each element in the set of matched elements. - * + * * param func A function that returns an HTML string, DOM element(s), or jQuery object to insert after each element in the set of matched elements. Receives the index position of the element in the set as an argument. Within the function, this refers to the current element in the set. */ - after(func: (index: number, html: string) => string|Element|JQuery): JQuery; + after(func: (index: number, html: string) => string | Element | JQuery): JQuery; /** * Insert content, specified by the parameter, to the end of each element in the set of matched elements. - * + * * param content1 DOM element, DocumentFragment, array of elements, HTML string, or jQuery object to insert at the end of each element in the set of matched elements. * param content2 One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the end of each element in the set of matched elements. */ - append(content1: JQuery|any[]|Element|DocumentFragment|Text|string, ...content2: any[]): JQuery; + append(content1: JQuery | any[] | Element | DocumentFragment | Text | string, ...content2: any[]): JQuery; /** * Insert content, specified by the parameter, to the end of each element in the set of matched elements. - * + * * param func A function that returns an HTML string, DOM element(s), or jQuery object to insert at the end of each element in the set of matched elements. Receives the index position of the element in the set and the old HTML value of the element as arguments. Within the function, this refers to the current element in the set. */ - append(func: (index: number, html: string) => string|Element|JQuery): JQuery; + append(func: (index: number, html: string) => string | Element | JQuery): JQuery; /** * Insert every element in the set of matched elements to the end of the target. - * + * * @param target A selector, element, HTML string, array of elements, or jQuery object; the matched set of elements will be inserted at the end of the element(s) specified by this parameter. */ - appendTo(target: JQuery|any[]|Element|string): JQuery; + appendTo(target: JQuery | any[] | Element | string): JQuery; /** * Insert content, specified by the parameter, before each element in the set of matched elements. - * + * * param content1 HTML string, DOM element, DocumentFragment, array of elements, or jQuery object to insert before each element in the set of matched elements. * param content2 One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert before each element in the set of matched elements. */ - before(content1: JQuery|any[]|Element|DocumentFragment|Text|string, ...content2: any[]): JQuery; + before(content1: JQuery | any[] | Element | DocumentFragment | Text | string, ...content2: any[]): JQuery; /** * Insert content, specified by the parameter, before each element in the set of matched elements. - * + * * param func A function that returns an HTML string, DOM element(s), or jQuery object to insert before each element in the set of matched elements. Receives the index position of the element in the set as an argument. Within the function, this refers to the current element in the set. */ - before(func: (index: number, html: string) => string|Element|JQuery): JQuery; + before(func: (index: number, html: string) => string | Element | JQuery): JQuery; /** * Create a deep copy of the set of matched elements. - * + * * param withDataAndEvents A Boolean indicating whether event handlers and data should be copied along with the elements. The default value is false. * param deepWithDataAndEvents A Boolean indicating whether event handlers and data for all children of the cloned element should be copied. By default its value matches the first argument's value (which defaults to false). */ @@ -2696,7 +2696,7 @@ interface JQuery { /** * Remove the set of matched elements from the DOM. - * + * * param selector A selector expression that filters the set of matched elements to be removed. */ detach(selector?: string): JQuery; @@ -2708,65 +2708,65 @@ interface JQuery { /** * Insert every element in the set of matched elements after the target. - * + * * param target A selector, element, array of elements, HTML string, or jQuery object; the matched set of elements will be inserted after the element(s) specified by this parameter. */ - insertAfter(target: JQuery|any[]|Element|Text|string): JQuery; + insertAfter(target: JQuery | any[] | Element | Text | string): JQuery; /** * Insert every element in the set of matched elements before the target. - * + * * param target A selector, element, array of elements, HTML string, or jQuery object; the matched set of elements will be inserted before the element(s) specified by this parameter. */ - insertBefore(target: JQuery|any[]|Element|Text|string): JQuery; + insertBefore(target: JQuery | any[] | Element | Text | string): JQuery; /** * Insert content, specified by the parameter, to the beginning of each element in the set of matched elements. - * + * * param content1 DOM element, DocumentFragment, array of elements, HTML string, or jQuery object to insert at the beginning of each element in the set of matched elements. * param content2 One or more additional DOM elements, arrays of elements, HTML strings, or jQuery objects to insert at the beginning of each element in the set of matched elements. */ - prepend(content1: JQuery|any[]|Element|DocumentFragment|Text|string, ...content2: any[]): JQuery; + prepend(content1: JQuery | any[] | Element | DocumentFragment | Text | string, ...content2: any[]): JQuery; /** * Insert content, specified by the parameter, to the beginning of each element in the set of matched elements. - * + * * param func A function that returns an HTML string, DOM element(s), or jQuery object to insert at the beginning of each element in the set of matched elements. Receives the index position of the element in the set and the old HTML value of the element as arguments. Within the function, this refers to the current element in the set. */ - prepend(func: (index: number, html: string) => string|Element|JQuery): JQuery; + prepend(func: (index: number, html: string) => string | Element | JQuery): JQuery; /** * Insert every element in the set of matched elements to the beginning of the target. - * + * * @param target A selector, element, HTML string, array of elements, or jQuery object; the matched set of elements will be inserted at the beginning of the element(s) specified by this parameter. */ - prependTo(target: JQuery|any[]|Element|string): JQuery; + prependTo(target: JQuery | any[] | Element | string): JQuery; /** * Remove the set of matched elements from the DOM. - * + * * @param selector A selector expression that filters the set of matched elements to be removed. */ remove(selector?: string): JQuery; /** * Replace each target element with the set of matched elements. - * + * * @param target A selector string, jQuery object, DOM element, or array of elements indicating which element(s) to replace. */ - replaceAll(target: JQuery|any[]|Element|string): JQuery; + replaceAll(target: JQuery | any[] | Element | string): JQuery; /** * Replace each element in the set of matched elements with the provided new content and return the set of elements that was removed. - * + * * param newContent The content to insert. May be an HTML string, DOM element, array of DOM elements, or jQuery object. */ - replaceWith(newContent: JQuery|any[]|Element|Text|string): JQuery; + replaceWith(newContent: JQuery | any[] | Element | Text | string): JQuery; /** * Replace each element in the set of matched elements with the provided new content and return the set of elements that was removed. - * + * * param func A function that returns content with which to replace the set of matched elements. */ - replaceWith(func: () => Element|JQuery): JQuery; + replaceWith(func: () => Element | JQuery): JQuery; /** * Get the combined text contents of each element in the set of matched elements, including their descendants. @@ -2774,13 +2774,13 @@ interface JQuery { text(): string; /** * Set the content of each element in the set of matched elements to the specified text. - * + * * @param text The text to set as the content of each matched element. When Number or Boolean is supplied, it will be converted to a String representation. */ - text(text: string|number|boolean): JQuery; + text(text: string | number | boolean): JQuery; /** * Set the content of each element in the set of matched elements to the specified text. - * + * * @param func A function returning the text content to set. Receives the index position of the element in the set and the old text value as arguments. */ text(func: (index: number, text: string) => string): JQuery; @@ -2798,48 +2798,48 @@ interface JQuery { /** * Wrap an HTML structure around each element in the set of matched elements. - * + * * @param wrappingElement A selector, element, HTML string, or jQuery object specifying the structure to wrap around the matched elements. */ - wrap(wrappingElement: JQuery|Element|string): JQuery; + wrap(wrappingElement: JQuery | Element | string): JQuery; /** * Wrap an HTML structure around each element in the set of matched elements. - * + * * @param func A callback function returning the HTML content or jQuery object to wrap around the matched elements. Receives the index position of the element in the set as an argument. Within the function, this refers to the current element in the set. */ - wrap(func: (index: number) => string|JQuery): JQuery; + wrap(func: (index: number) => string | JQuery): JQuery; /** * Wrap an HTML structure around all elements in the set of matched elements. - * + * * @param wrappingElement A selector, element, HTML string, or jQuery object specifying the structure to wrap around the matched elements. */ - wrapAll(wrappingElement: JQuery|Element|string): JQuery; + wrapAll(wrappingElement: JQuery | Element | string): JQuery; wrapAll(func: (index: number) => string): JQuery; /** * Wrap an HTML structure around the content of each element in the set of matched elements. - * + * * @param wrappingElement An HTML snippet, selector expression, jQuery object, or DOM element specifying the structure to wrap around the content of the matched elements. */ - wrapInner(wrappingElement: JQuery|Element|string): JQuery; + wrapInner(wrappingElement: JQuery | Element | string): JQuery; /** * Wrap an HTML structure around the content of each element in the set of matched elements. - * + * * @param func A callback function which generates a structure to wrap around the content of the matched elements. Receives the index position of the element in the set as an argument. Within the function, this refers to the current element in the set. */ wrapInner(func: (index: number) => string): JQuery; /** * Iterate over a jQuery object, executing a function for each matched element. - * + * * @param func A function to execute for each matched element. */ each(func: (index: number, elem: Element) => any): JQuery; /** * Retrieve one of the elements matched by the jQuery object. - * + * * @param index A zero-based integer indicating which element to retrieve. */ get(index: number): HTMLElement; @@ -2855,10 +2855,10 @@ interface JQuery { index(): number; /** * Search for a given element from among the matched elements. - * + * * @param selector A selector representing a jQuery collection in which to look for an element. */ - index(selector: string|JQuery|Element): number; + index(selector: string | JQuery | Element): number; /** * The number of elements in the jQuery object. @@ -2874,66 +2874,66 @@ interface JQuery { /** * Add elements to the set of matched elements. - * + * * @param selector A string representing a selector expression to find additional elements to add to the set of matched elements. * @param context The point in the document at which the selector should begin matching; similar to the context argument of the $(selector, context) method. */ add(selector: string, context?: Element): JQuery; /** * Add elements to the set of matched elements. - * + * * @param elements One or more elements to add to the set of matched elements. */ add(...elements: Element[]): JQuery; /** * Add elements to the set of matched elements. - * + * * @param html An HTML fragment to add to the set of matched elements. */ add(html: string): JQuery; /** * Add elements to the set of matched elements. - * + * * @param obj An existing jQuery object to add to the set of matched elements. */ add(obj: JQuery): JQuery; /** * Get the children of each element in the set of matched elements, optionally filtered by a selector. - * + * * @param selector A string containing a selector expression to match elements against. */ children(selector?: string): JQuery; /** * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. - * + * * @param selector A string containing a selector expression to match elements against. */ closest(selector: string): JQuery; /** * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. - * + * * @param selector A string containing a selector expression to match elements against. * @param context A DOM element within which a matching element may be found. If no context is passed in then the context of the jQuery set will be used instead. */ closest(selector: string, context?: Element): JQuery; /** * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. - * + * * @param obj A jQuery object to match elements against. */ closest(obj: JQuery): JQuery; /** * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. - * + * * @param element An element to match elements against. */ closest(element: Element): JQuery; /** * Get an array of all the elements and selectors matched against the current element up through the DOM tree. - * + * * @param selectors An array or string containing a selector expression to match elements against (can also be a jQuery object). * @param context A DOM element within which a matching element may be found. If no context is passed in then the context of the jQuery set will be used instead. */ @@ -2951,52 +2951,52 @@ interface JQuery { /** * Reduce the set of matched elements to the one at the specified index. - * + * * @param index An integer indicating the 0-based position of the element. OR An integer indicating the position of the element, counting backwards from the last element in the set. - * + * */ eq(index: number): JQuery; /** * Reduce the set of matched elements to those that match the selector or pass the function's test. - * + * * @param selector A string containing a selector expression to match the current set of elements against. */ filter(selector: string): JQuery; /** * Reduce the set of matched elements to those that match the selector or pass the function's test. - * + * * @param func A function used as a test for each element in the set. this is the current DOM element. */ filter(func: (index: number, element: Element) => any): JQuery; /** * Reduce the set of matched elements to those that match the selector or pass the function's test. - * + * * @param element An element to match the current set of elements against. */ filter(element: Element): JQuery; /** * Reduce the set of matched elements to those that match the selector or pass the function's test. - * + * * @param obj An existing jQuery object to match the current set of elements against. */ filter(obj: JQuery): JQuery; /** * Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element. - * + * * @param selector A string containing a selector expression to match elements against. */ find(selector: string): JQuery; /** * Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element. - * + * * @param element An element to match elements against. */ find(element: Element): JQuery; /** * Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element. - * + * * @param obj A jQuery object to match elements against. */ find(obj: JQuery): JQuery; @@ -3008,38 +3008,38 @@ interface JQuery { /** * Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element. - * + * * @param selector A string containing a selector expression to match elements against. */ has(selector: string): JQuery; /** * Reduce the set of matched elements to those that have a descendant that matches the selector or DOM element. - * + * * @param contained A DOM element to match elements against. */ has(contained: Element): JQuery; /** * Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments. - * + * * @param selector A string containing a selector expression to match elements against. */ is(selector: string): boolean; /** * Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments. - * + * * @param func A function used as a test for the set of elements. It accepts one argument, index, which is the element's index in the jQuery collection.Within the function, this refers to the current DOM element. */ is(func: (index: number, element: Element) => boolean): boolean; /** * Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments. - * + * * @param obj An existing jQuery object to match the current set of elements against. */ is(obj: JQuery): boolean; /** * Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments. - * + * * @param elements One or more elements to match the current set of elements against. */ is(elements: any): boolean; @@ -3051,42 +3051,42 @@ interface JQuery { /** * Pass each element in the current matched set through a function, producing a new jQuery object containing the return values. - * + * * @param callback A function object that will be invoked for each element in the current set. */ map(callback: (index: number, domElement: Element) => any): JQuery; /** * Get the immediately following sibling of each element in the set of matched elements. If a selector is provided, it retrieves the next sibling only if it matches that selector. - * + * * @param selector A string containing a selector expression to match elements against. */ next(selector?: string): JQuery; /** * Get all following siblings of each element in the set of matched elements, optionally filtered by a selector. - * + * * @param selector A string containing a selector expression to match elements against. */ nextAll(selector?: string): JQuery; /** * Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed. - * + * * @param selector A string containing a selector expression to indicate where to stop matching following sibling elements. * @param filter A string containing a selector expression to match elements against. */ nextUntil(selector?: string, filter?: string): JQuery; /** * Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed. - * + * * @param element A DOM node or jQuery object indicating where to stop matching following sibling elements. * @param filter A string containing a selector expression to match elements against. */ nextUntil(element?: Element, filter?: string): JQuery; /** * Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed. - * + * * @param obj A DOM node or jQuery object indicating where to stop matching following sibling elements. * @param filter A string containing a selector expression to match elements against. */ @@ -3094,25 +3094,25 @@ interface JQuery { /** * Remove elements from the set of matched elements. - * + * * @param selector A string containing a selector expression to match elements against. */ not(selector: string): JQuery; /** * Remove elements from the set of matched elements. - * + * * @param func A function used as a test for each element in the set. this is the current DOM element. */ not(func: (index: number, element: Element) => boolean): JQuery; /** * Remove elements from the set of matched elements. - * + * * @param elements One or more DOM elements to remove from the matched set. */ - not(elements: Element|Element[]): JQuery; + not(elements: Element | Element[]): JQuery; /** * Remove elements from the set of matched elements. - * + * * @param obj An existing jQuery object to match the current set of elements against. */ not(obj: JQuery): JQuery; @@ -3124,35 +3124,35 @@ interface JQuery { /** * Get the parent of each element in the current set of matched elements, optionally filtered by a selector. - * + * * @param selector A string containing a selector expression to match elements against. */ parent(selector?: string): JQuery; /** * Get the ancestors of each element in the current set of matched elements, optionally filtered by a selector. - * + * * @param selector A string containing a selector expression to match elements against. */ parents(selector?: string): JQuery; /** * Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object. - * + * * @param selector A string containing a selector expression to indicate where to stop matching ancestor elements. * @param filter A string containing a selector expression to match elements against. */ parentsUntil(selector?: string, filter?: string): JQuery; /** * Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object. - * + * * @param element A DOM node or jQuery object indicating where to stop matching ancestor elements. * @param filter A string containing a selector expression to match elements against. */ parentsUntil(element?: Element, filter?: string): JQuery; /** * Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object. - * + * * @param obj A DOM node or jQuery object indicating where to stop matching ancestor elements. * @param filter A string containing a selector expression to match elements against. */ @@ -3160,35 +3160,35 @@ interface JQuery { /** * Get the immediately preceding sibling of each element in the set of matched elements, optionally filtered by a selector. - * + * * @param selector A string containing a selector expression to match elements against. */ prev(selector?: string): JQuery; /** * Get all preceding siblings of each element in the set of matched elements, optionally filtered by a selector. - * + * * @param selector A string containing a selector expression to match elements against. */ prevAll(selector?: string): JQuery; /** * Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object. - * + * * @param selector A string containing a selector expression to indicate where to stop matching preceding sibling elements. * @param filter A string containing a selector expression to match elements against. */ prevUntil(selector?: string, filter?: string): JQuery; /** * Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object. - * + * * @param element A DOM node or jQuery object indicating where to stop matching preceding sibling elements. * @param filter A string containing a selector expression to match elements against. */ prevUntil(element?: Element, filter?: string): JQuery; /** * Get all preceding siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object. - * + * * @param obj A DOM node or jQuery object indicating where to stop matching preceding sibling elements. * @param filter A string containing a selector expression to match elements against. */ @@ -3196,14 +3196,14 @@ interface JQuery { /** * Get the siblings of each element in the set of matched elements, optionally filtered by a selector. - * + * * @param selector A string containing a selector expression to match elements against. */ siblings(selector?: string): JQuery; /** * Reduce the set of matched elements to a subset specified by a range of indices. - * + * * @param start An integer indicating the 0-based position at which the elements begin to be selected. If negative, it indicates an offset from the end of the set. * @param end An integer indicating the 0-based position at which the elements stop being selected. If negative, it indicates an offset from the end of the set. If omitted, the range continues until the end of the set. */ @@ -3211,39 +3211,39 @@ interface JQuery { /** * Show the queue of functions to be executed on the matched elements. - * + * * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue. */ queue(queueName?: string): any[]; /** * Manipulate the queue of functions to be executed, once for each matched element. - * + * * @param newQueue An array of functions to replace the current queue contents. */ queue(newQueue: Function[]): JQuery; /** * Manipulate the queue of functions to be executed, once for each matched element. - * + * * @param callback The new function to add to the queue, with a function to call that will dequeue the next item. */ queue(callback: Function): JQuery; /** * Manipulate the queue of functions to be executed, once for each matched element. - * + * * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue. * @param newQueue An array of functions to replace the current queue contents. */ queue(queueName: string, newQueue: Function[]): JQuery; /** * Manipulate the queue of functions to be executed, once for each matched element. - * + * * @param queueName A string containing the name of the queue. Defaults to fx, the standard effects queue. * @param callback The new function to add to the queue, with a function to call that will dequeue the next item. */ queue(queueName: string, callback: Function): JQuery; } -declare module "jquery" { +declare module 'jquery' { export = $; } -declare var jQuery: JQueryStatic; -declare var $: JQueryStatic; \ No newline at end of file +declare const jQuery: JQueryStatic; +declare const $: JQueryStatic; diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index dc078cfcb7108..ebb1c9f9bfc3a 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -7,20 +7,20 @@ "engines": { "node": "*" }, - "main": "./out/htmlServerMain", + "main": "./out/node/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.3-next.19", - "vscode-html-languageservice": "^3.0.4-next.8", - "vscode-languageserver": "^6.0.0-next.3", - "vscode-nls": "^4.1.1", - "vscode-uri": "^2.0.3" + "vscode-css-languageservice": "^4.3.3", + "vscode-html-languageservice": "^3.1.2", + "vscode-languageserver": "7.0.0-next.3", + "vscode-nls": "^4.1.2", + "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": { @@ -30,6 +30,6 @@ "install-service-local": "npm install ../../../../vscode-css-languageservice -f && npm install ../../../../vscode-html-languageservice -f", "install-server-next": "yarn add vscode-languageserver@next", "install-server-local": "npm install ../../../../vscode-languageserver-node/server -f", - "test": "npm run compile && npx mocha" + "test": "npm run compile && node ./test/index.js" } } diff --git a/extensions/html-language-features/server/src/browser/htmlServerMain.ts b/extensions/html-language-features/server/src/browser/htmlServerMain.ts new file mode 100644 index 0000000000000..1d38d33db3787 --- /dev/null +++ b/extensions/html-language-features/server/src/browser/htmlServerMain.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 '../htmlServer'; + +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/html-language-features/server/src/customData.ts b/extensions/html-language-features/server/src/customData.ts index 1d550eddf9fdf..3ef347a23d2ef 100644 --- a/extensions/html-language-features/server/src/customData.ts +++ b/extensions/html-language-features/server/src/customData.ts @@ -4,26 +4,35 @@ *--------------------------------------------------------------------------------------------*/ import { IHTMLDataProvider, newHTMLDataProvider } from 'vscode-html-languageservice'; -import * as fs from 'fs'; +import { RequestService } from './requests'; -export function getDataProviders(dataPaths?: string[]): IHTMLDataProvider[] { - if (!dataPaths) { - return []; - } +export function fetchHTMLDataProviders(dataPaths: string[], requestService: RequestService): Promise { + const providers = dataPaths.map(async p => { + try { + const content = await requestService.getContent(p); + return parseHTMLData(p, content); + } catch (e) { + return newHTMLDataProvider(p, { version: 1 }); + } + }); - const providers: IHTMLDataProvider[] = []; + return Promise.all(providers); +} - dataPaths.forEach((path, i) => { - try { - if (fs.existsSync(path)) { - const htmlData = JSON.parse(fs.readFileSync(path, 'utf-8')); +function parseHTMLData(id: string, source: string): IHTMLDataProvider { + let rawData: any; - providers.push(newHTMLDataProvider(`customProvider${i}`, htmlData)); - } - } catch (err) { - console.log(`Failed to load tag from ${path}`); - } + try { + rawData = JSON.parse(source); + } catch (err) { + return newHTMLDataProvider(id, { version: 1 }); + } + + return newHTMLDataProvider(id, { + version: rawData.version || 1, + tags: rawData.tags || [], + globalAttributes: rawData.globalAttributes || [], + valueSets: rawData.valueSets || [] }); +} - return providers; -} \ No newline at end of file diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts new file mode 100644 index 0000000000000..ade37f2253366 --- /dev/null +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -0,0 +1,559 @@ +/*--------------------------------------------------------------------------------------------- + * 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, RequestType, + DocumentRangeFormattingRequest, Disposable, DocumentSelector, TextDocumentPositionParams, ServerCapabilities, + ConfigurationRequest, ConfigurationParams, DidChangeWorkspaceFoldersNotification, + DocumentColorRequest, ColorPresentationRequest, TextDocumentSyncKind, NotificationType +} from 'vscode-languageserver'; +import { + getLanguageModes, LanguageModes, Settings, TextDocument, Position, Diagnostic, WorkspaceFolder, ColorInformation, + Range, DocumentLink, SymbolInformation, TextDocumentIdentifier +} from './modes/languageModes'; + +import { format } from './modes/formatting'; +import { pushAll } from './utils/arrays'; +import { getDocumentContext } from './utils/documentContext'; +import { URI } from 'vscode-uri'; +import { formatError, runSafe } from './utils/runner'; + +import { getFoldingRanges } from './modes/htmlFolding'; +import { fetchHTMLDataProviders } from './customData'; +import { getSelectionRanges } from './modes/selectionRanges'; +import { SemanticTokenProvider, newSemanticTokenProvider } from './modes/semanticTokens'; +import { RequestService, getRequestService } from './requests'; + +namespace CustomDataChangedNotification { + export const type: NotificationType = new NotificationType('html/customDataChanged'); +} + +namespace TagCloseRequest { + export const type: RequestType = new RequestType('html/tag'); +} +namespace OnTypeRenameRequest { + export const type: RequestType = new RequestType('html/onTypeRename'); +} + +// experimental: semantic tokens +interface SemanticTokenParams { + textDocument: TextDocumentIdentifier; + ranges?: Range[]; +} +namespace SemanticTokenRequest { + export const type: RequestType = new RequestType('html/semanticTokens'); +} +namespace SemanticTokenLegendRequest { + export const type: RequestType = new RequestType('html/semanticTokenLegend'); +} + +export interface RuntimeEnvironment { + file?: RequestService; + http?: RequestService + configureHttpRequests?(proxy: string, strictSSL: boolean): void; +} + +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); + + let workspaceFolders: WorkspaceFolder[] = []; + + let languageModes: LanguageModes; + + let clientSnippetSupport = false; + let dynamicFormatterRegistration = false; + let scopedSettingsSupport = false; + let workspaceFoldersSupport = false; + let foldingRangeLimit = Number.MAX_VALUE; + + const notReady = () => Promise.reject('Not Ready'); + let requestService: RequestService = { getContent: notReady, stat: notReady, readDirectory: notReady }; + + + + let globalSettings: Settings = {}; + let documentSettings: { [key: string]: Thenable } = {}; + // remove document settings on close + documents.onDidClose(e => { + delete documentSettings[e.document.uri]; + }); + + function getDocumentSettings(textDocument: TextDocument, needsDocumentSettings: () => boolean): Thenable { + if (scopedSettingsSupport && needsDocumentSettings()) { + let promise = documentSettings[textDocument.uri]; + if (!promise) { + const scopeUri = textDocument.uri; + const configRequestParam: ConfigurationParams = { items: [{ scopeUri, section: 'css' }, { scopeUri, section: 'html' }, { scopeUri, section: 'javascript' }] }; + promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => ({ css: s[0], html: s[1], javascript: s[2] })); + documentSettings[textDocument.uri] = promise; + } + return promise; + } + return Promise.resolve(undefined); + } + + // 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 initializationOptions = params.initializationOptions; + + 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); + + const workspace = { + get settings() { return globalSettings; }, + get folders() { return workspaceFolders; } + }; + + languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, params.capabilities, requestService); + + const dataPaths: string[] = params.initializationOptions.dataPaths || []; + fetchHTMLDataProviders(dataPaths, requestService).then(dataProviders => { + languageModes.updateDataProviders(dataProviders); + }); + + documents.onDidClose(e => { + languageModes.onDocumentRemoved(e.document); + }); + connection.onShutdown(() => { + languageModes.dispose(); + }); + + 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'); + scopedSettingsSupport = getClientCapability('workspace.configuration', false); + workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false); + foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + const capabilities: ServerCapabilities = { + textDocumentSync: TextDocumentSyncKind.Incremental, + completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined, + hoverProvider: true, + documentHighlightProvider: true, + documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true, + documentLinkProvider: { resolveProvider: false }, + documentSymbolProvider: true, + definitionProvider: true, + signatureHelpProvider: { triggerCharacters: ['('] }, + referencesProvider: true, + colorProvider: {}, + foldingRangeProvider: true, + selectionRangeProvider: true, + renameProvider: true + }; + return { capabilities }; + }); + + connection.onInitialized(() => { + if (workspaceFoldersSupport) { + connection.client.register(DidChangeWorkspaceFoldersNotification.type); + + connection.onNotification(DidChangeWorkspaceFoldersNotification.type, e => { + const toAdd = e.event.added; + const toRemove = e.event.removed; + const updatedFolders = []; + if (workspaceFolders) { + for (const folder of workspaceFolders) { + if (!toRemove.some(r => r.uri === folder.uri) && !toAdd.some(r => r.uri === folder.uri)) { + updatedFolders.push(folder); + } + } + } + workspaceFolders = updatedFolders.concat(toAdd); + documents.all().forEach(triggerValidation); + }); + } + }); + + let formatterRegistration: Thenable | null = null; + + // The settings have changed. Is send on server activation as well. + connection.onDidChangeConfiguration((change) => { + globalSettings = change.settings; + documentSettings = {}; // reset all document settings + documents.all().forEach(triggerValidation); + + // dynamically enable & disable the formatter + if (dynamicFormatterRegistration) { + const enableFormatter = globalSettings && globalSettings.html && globalSettings.html.format && globalSettings.html.format.enable; + if (enableFormatter) { + if (!formatterRegistration) { + const documentSelector: DocumentSelector = [{ language: 'html' }, { language: 'handlebars' }]; + formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector }); + } + } else if (formatterRegistration) { + formatterRegistration.then(r => r.dispose()); + formatterRegistration = null; + } + } + }); + + 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 isValidationEnabled(languageId: string, settings: Settings = globalSettings) { + const validationSettings = settings && settings.html && settings.html.validate; + if (validationSettings) { + return languageId === 'css' && validationSettings.styles !== false || languageId === 'javascript' && validationSettings.scripts !== false; + } + return true; + } + + async function validateTextDocument(textDocument: TextDocument) { + try { + const version = textDocument.version; + const diagnostics: Diagnostic[] = []; + if (textDocument.languageId === 'html') { + const modes = languageModes.getAllModesInDocument(textDocument); + const settings = await getDocumentSettings(textDocument, () => modes.some(m => !!m.doValidation)); + const latestTextDocument = documents.get(textDocument.uri); + if (latestTextDocument && latestTextDocument.version === version) { // check no new version has come in after in after the async op + for (const mode of modes) { + if (mode.doValidation && isValidationEnabled(mode.getId(), settings)) { + pushAll(diagnostics, await mode.doValidation(latestTextDocument, settings)); + } + } + connection.sendDiagnostics({ uri: latestTextDocument.uri, diagnostics }); + } + } + } catch (e) { + connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); + } + } + + connection.onCompletion(async (textDocumentPosition, token) => { + return runSafe(async () => { + const document = documents.get(textDocumentPosition.textDocument.uri); + if (!document) { + return null; + } + const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); + if (!mode || !mode.doComplete) { + return { isIncomplete: true, items: [] }; + } + const doComplete = mode.doComplete!; + + if (mode.getId() !== 'html') { + /* __GDPR__ + "html.embbedded.complete" : { + "languageId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + connection.telemetry.logEvent({ key: 'html.embbedded.complete', value: { languageId: mode.getId() } }); + } + + const settings = await getDocumentSettings(document, () => doComplete.length > 2); + const documentContext = getDocumentContext(document.uri, workspaceFolders); + return doComplete(document, textDocumentPosition.position, documentContext, settings); + + }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); + }); + + connection.onCompletionResolve((item, token) => { + return runSafe(async () => { + const data = item.data; + if (data && data.languageId && data.uri) { + const mode = languageModes.getMode(data.languageId); + const document = documents.get(data.uri); + if (mode && mode.doResolve && document) { + return mode.doResolve(document, item); + } + } + return item; + }, item, `Error while resolving completion proposal`, token); + }); + + connection.onHover((textDocumentPosition, token) => { + return runSafe(async () => { + const document = documents.get(textDocumentPosition.textDocument.uri); + if (document) { + const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); + if (mode && mode.doHover) { + return mode.doHover(document, textDocumentPosition.position); + } + } + return null; + }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token); + }); + + connection.onDocumentHighlight((documentHighlightParams, token) => { + return runSafe(async () => { + const document = documents.get(documentHighlightParams.textDocument.uri); + if (document) { + const mode = languageModes.getModeAtPosition(document, documentHighlightParams.position); + if (mode && mode.findDocumentHighlight) { + return mode.findDocumentHighlight(document, documentHighlightParams.position); + } + } + return []; + }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token); + }); + + connection.onDefinition((definitionParams, token) => { + return runSafe(async () => { + const document = documents.get(definitionParams.textDocument.uri); + if (document) { + const mode = languageModes.getModeAtPosition(document, definitionParams.position); + if (mode && mode.findDefinition) { + return mode.findDefinition(document, definitionParams.position); + } + } + return []; + }, null, `Error while computing definitions for ${definitionParams.textDocument.uri}`, token); + }); + + connection.onReferences((referenceParams, token) => { + return runSafe(async () => { + const document = documents.get(referenceParams.textDocument.uri); + if (document) { + const mode = languageModes.getModeAtPosition(document, referenceParams.position); + if (mode && mode.findReferences) { + return mode.findReferences(document, referenceParams.position); + } + } + return []; + }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token); + }); + + connection.onSignatureHelp((signatureHelpParms, token) => { + return runSafe(async () => { + const document = documents.get(signatureHelpParms.textDocument.uri); + if (document) { + const mode = languageModes.getModeAtPosition(document, signatureHelpParms.position); + if (mode && mode.doSignatureHelp) { + return mode.doSignatureHelp(document, signatureHelpParms.position); + } + } + return null; + }, null, `Error while computing signature help for ${signatureHelpParms.textDocument.uri}`, token); + }); + + connection.onDocumentRangeFormatting(async (formatParams, token) => { + return runSafe(async () => { + const document = documents.get(formatParams.textDocument.uri); + if (document) { + let settings = await getDocumentSettings(document, () => true); + if (!settings) { + settings = globalSettings; + } + const unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || ''; + const enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) }; + + return format(languageModes, document, formatParams.range, formatParams.options, settings, enabledModes); + } + return []; + }, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token); + }); + + connection.onDocumentLinks((documentLinkParam, token) => { + return runSafe(async () => { + const document = documents.get(documentLinkParam.textDocument.uri); + const links: DocumentLink[] = []; + if (document) { + const documentContext = getDocumentContext(document.uri, workspaceFolders); + for (const m of languageModes.getAllModesInDocument(document)) { + if (m.findDocumentLinks) { + pushAll(links, await m.findDocumentLinks(document, documentContext)); + } + } + } + return links; + }, [], `Error while document links for ${documentLinkParam.textDocument.uri}`, token); + }); + + connection.onDocumentSymbol((documentSymbolParms, token) => { + return runSafe(async () => { + const document = documents.get(documentSymbolParms.textDocument.uri); + const symbols: SymbolInformation[] = []; + if (document) { + for (const m of languageModes.getAllModesInDocument(document)) { + if (m.findDocumentSymbols) { + pushAll(symbols, await m.findDocumentSymbols(document)); + } + } + } + return symbols; + }, [], `Error while computing document symbols for ${documentSymbolParms.textDocument.uri}`, token); + }); + + connection.onRequest(DocumentColorRequest.type, (params, token) => { + return runSafe(async () => { + const infos: ColorInformation[] = []; + const document = documents.get(params.textDocument.uri); + if (document) { + for (const m of languageModes.getAllModesInDocument(document)) { + if (m.findDocumentColors) { + pushAll(infos, await m.findDocumentColors(document)); + } + } + } + return infos; + }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); + }); + + connection.onRequest(ColorPresentationRequest.type, (params, token) => { + return runSafe(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + const mode = languageModes.getModeAtPosition(document, params.range.start); + if (mode && mode.getColorPresentations) { + return mode.getColorPresentations(document, params.color, params.range); + } + } + return []; + }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); + }); + + connection.onRequest(TagCloseRequest.type, (params, token) => { + return runSafe(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + const pos = params.position; + if (pos.character > 0) { + const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1)); + if (mode && mode.doAutoClose) { + return mode.doAutoClose(document, pos); + } + } + } + return null; + }, null, `Error while computing tag close actions for ${params.textDocument.uri}`, token); + }); + + connection.onFoldingRanges((params, token) => { + return runSafe(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return getFoldingRanges(languageModes, document, foldingRangeLimit, token); + } + return null; + }, null, `Error while computing folding regions for ${params.textDocument.uri}`, token); + }); + + connection.onSelectionRanges((params, token) => { + return runSafe(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return getSelectionRanges(languageModes, document, params.positions); + } + return []; + }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); + }); + + connection.onRenameRequest((params, token) => { + return runSafe(async () => { + const document = documents.get(params.textDocument.uri); + const position: Position = params.position; + + if (document) { + const htmlMode = languageModes.getMode('html'); + if (htmlMode && htmlMode.doRename) { + return htmlMode.doRename(document, position, params.newName); + } + } + return null; + }, null, `Error while computing rename for ${params.textDocument.uri}`, token); + }); + + connection.onRequest(OnTypeRenameRequest.type, (params, token) => { + return runSafe(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + const pos = params.position; + if (pos.character > 0) { + const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1)); + if (mode && mode.doOnTypeRename) { + return mode.doOnTypeRename(document, pos); + } + } + } + return null; + }, null, `Error while computing synced regions for ${params.textDocument.uri}`, token); + }); + + let semanticTokensProvider: SemanticTokenProvider | undefined; + function getSemanticTokenProvider() { + if (!semanticTokensProvider) { + semanticTokensProvider = newSemanticTokenProvider(languageModes); + } + return semanticTokensProvider; + } + + connection.onRequest(SemanticTokenRequest.type, (params, token) => { + return runSafe(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + return getSemanticTokenProvider().getSemanticTokens(document, params.ranges); + } + return null; + }, null, `Error while computing semantic tokens for ${params.textDocument.uri}`, token); + }); + + connection.onRequest(SemanticTokenLegendRequest.type, (_params, token) => { + return runSafe(async () => { + return getSemanticTokenProvider().legend; + }, null, `Error while computing semantic tokens legend`, token); + }); + + connection.onNotification(CustomDataChangedNotification.type, dataPaths => { + fetchHTMLDataProviders(dataPaths, requestService).then(dataProviders => { + languageModes.updateDataProviders(dataProviders); + }); + }); + + // Listen on the connection + connection.listen(); +} diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts deleted file mode 100644 index 1e6e7e2e0ce83..0000000000000 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ /dev/null @@ -1,474 +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, RequestType, - DocumentRangeFormattingRequest, Disposable, DocumentSelector, TextDocumentPositionParams, ServerCapabilities, - Position, ConfigurationRequest, ConfigurationParams, DidChangeWorkspaceFoldersNotification, - WorkspaceFolder, DocumentColorRequest, ColorInformation, ColorPresentationRequest, TextDocumentSyncKind -} from 'vscode-languageserver'; -import { TextDocument, Diagnostic, DocumentLink, SymbolInformation } from 'vscode-html-languageservice'; -import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes'; - -import { format } from './modes/formatting'; -import { pushAll } from './utils/arrays'; -import { getDocumentContext } from './utils/documentContext'; -import { URI } from 'vscode-uri'; -import { formatError, runSafe, runSafeAsync } from './utils/runner'; - -import { getFoldingRanges } from './modes/htmlFolding'; -import { getDataProviders } from './customData'; - -namespace TagCloseRequest { - export const type: RequestType = new RequestType('html/tag'); -} - -// 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) => { - console.error(formatError(`Unhandled exception`, e)); -}); -process.on('uncaughtException', (e: any) => { - 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); - -let workspaceFolders: WorkspaceFolder[] = []; - -let languageModes: LanguageModes; - -let clientSnippetSupport = false; -let dynamicFormatterRegistration = false; -let scopedSettingsSupport = false; -let workspaceFoldersSupport = false; -let foldingRangeLimit = Number.MAX_VALUE; - -let globalSettings: Settings = {}; -let documentSettings: { [key: string]: Thenable } = {}; -// remove document settings on close -documents.onDidClose(e => { - delete documentSettings[e.document.uri]; -}); - -function getDocumentSettings(textDocument: TextDocument, needsDocumentSettings: () => boolean): Thenable { - if (scopedSettingsSupport && needsDocumentSettings()) { - let promise = documentSettings[textDocument.uri]; - if (!promise) { - const scopeUri = textDocument.uri; - const configRequestParam: ConfigurationParams = { items: [{ scopeUri, section: 'css' }, { scopeUri, section: 'html' }, { scopeUri, section: 'javascript' }] }; - promise = connection.sendRequest(ConfigurationRequest.type, configRequestParam).then(s => ({ css: s[0], html: s[1], javascript: s[2] })); - documentSettings[textDocument.uri] = promise; - } - return promise; - } - return Promise.resolve(undefined); -} - -// 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 initializationOptions = params.initializationOptions; - - 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 providers = getDataProviders(dataPaths); - - const workspace = { - get settings() { return globalSettings; }, - get folders() { return workspaceFolders; } - }; - - languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, params.capabilities, providers); - - documents.onDidClose(e => { - languageModes.onDocumentRemoved(e.document); - }); - connection.onShutdown(() => { - languageModes.dispose(); - }); - - 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'); - scopedSettingsSupport = getClientCapability('workspace.configuration', false); - workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false); - foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - const capabilities: ServerCapabilities = { - textDocumentSync: TextDocumentSyncKind.Incremental, - completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined, - hoverProvider: true, - documentHighlightProvider: true, - documentRangeFormattingProvider: params.initializationOptions.provideFormatter === true, - documentLinkProvider: { resolveProvider: false }, - documentSymbolProvider: true, - definitionProvider: true, - signatureHelpProvider: { triggerCharacters: ['('] }, - referencesProvider: true, - colorProvider: {}, - foldingRangeProvider: true, - selectionRangeProvider: true, - }; - return { capabilities }; -}); - -connection.onInitialized(() => { - if (workspaceFoldersSupport) { - connection.client.register(DidChangeWorkspaceFoldersNotification.type); - - connection.onNotification(DidChangeWorkspaceFoldersNotification.type, e => { - const toAdd = e.event.added; - const toRemove = e.event.removed; - const updatedFolders = []; - if (workspaceFolders) { - for (const folder of workspaceFolders) { - if (!toRemove.some(r => r.uri === folder.uri) && !toAdd.some(r => r.uri === folder.uri)) { - updatedFolders.push(folder); - } - } - } - workspaceFolders = updatedFolders.concat(toAdd); - documents.all().forEach(triggerValidation); - }); - } -}); - -let formatterRegistration: Thenable | null = null; - -// The settings have changed. Is send on server activation as well. -connection.onDidChangeConfiguration((change) => { - globalSettings = change.settings; - documentSettings = {}; // reset all document settings - documents.all().forEach(triggerValidation); - - // dynamically enable & disable the formatter - if (dynamicFormatterRegistration) { - const enableFormatter = globalSettings && globalSettings.html && globalSettings.html.format && globalSettings.html.format.enable; - if (enableFormatter) { - if (!formatterRegistration) { - const documentSelector: DocumentSelector = [{ language: 'html' }, { language: 'handlebars' }]; - formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector }); - } - } else if (formatterRegistration) { - formatterRegistration.then(r => r.dispose()); - formatterRegistration = null; - } - } -}); - -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 isValidationEnabled(languageId: string, settings: Settings = globalSettings) { - const validationSettings = settings && settings.html && settings.html.validate; - if (validationSettings) { - return languageId === 'css' && validationSettings.styles !== false || languageId === 'javascript' && validationSettings.scripts !== false; - } - return true; -} - -async function validateTextDocument(textDocument: TextDocument) { - try { - const version = textDocument.version; - const diagnostics: Diagnostic[] = []; - if (textDocument.languageId === 'html') { - const modes = languageModes.getAllModesInDocument(textDocument); - const settings = await getDocumentSettings(textDocument, () => modes.some(m => !!m.doValidation)); - const latestTextDocument = documents.get(textDocument.uri); - if (latestTextDocument && latestTextDocument.version === version) { // check no new version has come in after in after the async op - modes.forEach(mode => { - if (mode.doValidation && isValidationEnabled(mode.getId(), settings)) { - pushAll(diagnostics, mode.doValidation(latestTextDocument, settings)); - } - }); - connection.sendDiagnostics({ uri: latestTextDocument.uri, diagnostics }); - } - } - } catch (e) { - connection.console.error(formatError(`Error while validating ${textDocument.uri}`, e)); - } -} - -connection.onCompletion(async (textDocumentPosition, token) => { - return runSafeAsync(async () => { - const document = documents.get(textDocumentPosition.textDocument.uri); - if (!document) { - return null; - } - const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); - if (!mode || !mode.doComplete) { - return { isIncomplete: true, items: [] }; - } - const doComplete = mode.doComplete!; - - if (mode.getId() !== 'html') { - /* __GDPR__ - "html.embbedded.complete" : { - "languageId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - connection.telemetry.logEvent({ key: 'html.embbedded.complete', value: { languageId: mode.getId() } }); - } - - const settings = await getDocumentSettings(document, () => doComplete.length > 2); - const result = doComplete(document, textDocumentPosition.position, settings); - return result; - - }, null, `Error while computing completions for ${textDocumentPosition.textDocument.uri}`, token); -}); - -connection.onCompletionResolve((item, token) => { - return runSafe(() => { - const data = item.data; - if (data && data.languageId && data.uri) { - const mode = languageModes.getMode(data.languageId); - const document = documents.get(data.uri); - if (mode && mode.doResolve && document) { - return mode.doResolve(document, item); - } - } - return item; - }, item, `Error while resolving completion proposal`, token); -}); - -connection.onHover((textDocumentPosition, token) => { - return runSafe(() => { - const document = documents.get(textDocumentPosition.textDocument.uri); - if (document) { - const mode = languageModes.getModeAtPosition(document, textDocumentPosition.position); - if (mode && mode.doHover) { - return mode.doHover(document, textDocumentPosition.position); - } - } - return null; - }, null, `Error while computing hover for ${textDocumentPosition.textDocument.uri}`, token); -}); - -connection.onDocumentHighlight((documentHighlightParams, token) => { - return runSafe(() => { - const document = documents.get(documentHighlightParams.textDocument.uri); - if (document) { - const mode = languageModes.getModeAtPosition(document, documentHighlightParams.position); - if (mode && mode.findDocumentHighlight) { - return mode.findDocumentHighlight(document, documentHighlightParams.position); - } - } - return []; - }, [], `Error while computing document highlights for ${documentHighlightParams.textDocument.uri}`, token); -}); - -connection.onDefinition((definitionParams, token) => { - return runSafe(() => { - const document = documents.get(definitionParams.textDocument.uri); - if (document) { - const mode = languageModes.getModeAtPosition(document, definitionParams.position); - if (mode && mode.findDefinition) { - return mode.findDefinition(document, definitionParams.position); - } - } - return []; - }, null, `Error while computing definitions for ${definitionParams.textDocument.uri}`, token); -}); - -connection.onReferences((referenceParams, token) => { - return runSafe(() => { - const document = documents.get(referenceParams.textDocument.uri); - if (document) { - const mode = languageModes.getModeAtPosition(document, referenceParams.position); - if (mode && mode.findReferences) { - return mode.findReferences(document, referenceParams.position); - } - } - return []; - }, [], `Error while computing references for ${referenceParams.textDocument.uri}`, token); -}); - -connection.onSignatureHelp((signatureHelpParms, token) => { - return runSafe(() => { - const document = documents.get(signatureHelpParms.textDocument.uri); - if (document) { - const mode = languageModes.getModeAtPosition(document, signatureHelpParms.position); - if (mode && mode.doSignatureHelp) { - return mode.doSignatureHelp(document, signatureHelpParms.position); - } - } - return null; - }, null, `Error while computing signature help for ${signatureHelpParms.textDocument.uri}`, token); -}); - -connection.onDocumentRangeFormatting(async (formatParams, token) => { - return runSafeAsync(async () => { - const document = documents.get(formatParams.textDocument.uri); - if (document) { - let settings = await getDocumentSettings(document, () => true); - if (!settings) { - settings = globalSettings; - } - const unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || ''; - const enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) }; - - return format(languageModes, document, formatParams.range, formatParams.options, settings, enabledModes); - } - return []; - }, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token); -}); - -connection.onDocumentLinks((documentLinkParam, token) => { - return runSafe(() => { - const document = documents.get(documentLinkParam.textDocument.uri); - const links: DocumentLink[] = []; - if (document) { - const documentContext = getDocumentContext(document.uri, workspaceFolders); - languageModes.getAllModesInDocument(document).forEach(m => { - if (m.findDocumentLinks) { - pushAll(links, m.findDocumentLinks(document, documentContext)); - } - }); - } - return links; - }, [], `Error while document links for ${documentLinkParam.textDocument.uri}`, token); -}); - -connection.onDocumentSymbol((documentSymbolParms, token) => { - return runSafe(() => { - const document = documents.get(documentSymbolParms.textDocument.uri); - const symbols: SymbolInformation[] = []; - if (document) { - languageModes.getAllModesInDocument(document).forEach(m => { - if (m.findDocumentSymbols) { - pushAll(symbols, m.findDocumentSymbols(document)); - } - }); - } - return symbols; - }, [], `Error while computing document symbols for ${documentSymbolParms.textDocument.uri}`, token); -}); - -connection.onRequest(DocumentColorRequest.type, (params, token) => { - return runSafe(() => { - const infos: ColorInformation[] = []; - const document = documents.get(params.textDocument.uri); - if (document) { - languageModes.getAllModesInDocument(document).forEach(m => { - if (m.findDocumentColors) { - pushAll(infos, m.findDocumentColors(document)); - } - }); - } - return infos; - }, [], `Error while computing document colors for ${params.textDocument.uri}`, token); -}); - -connection.onRequest(ColorPresentationRequest.type, (params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const mode = languageModes.getModeAtPosition(document, params.range.start); - if (mode && mode.getColorPresentations) { - return mode.getColorPresentations(document, params.color, params.range); - } - } - return []; - }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); -}); - -connection.onRequest(TagCloseRequest.type, (params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - const pos = params.position; - if (pos.character > 0) { - const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1)); - if (mode && mode.doAutoClose) { - return mode.doAutoClose(document, pos); - } - } - } - return null; - }, null, `Error while computing tag close actions for ${params.textDocument.uri}`, token); -}); - -connection.onFoldingRanges((params, token) => { - return runSafe(() => { - const document = documents.get(params.textDocument.uri); - if (document) { - return getFoldingRanges(languageModes, document, foldingRangeLimit, token); - } - return null; - }, null, `Error while computing folding regions 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 htmlMode = languageModes.getMode('html'); - if (htmlMode && htmlMode.getSelectionRanges) { - return htmlMode.getSelectionRanges(document, positions); - } - } - return []; - }, [], `Error while computing selection ranges for ${params.textDocument.uri}`, token); -}); - - -// Listen on the connection -connection.listen(); diff --git a/extensions/html-language-features/server/src/languageModelCache.ts b/extensions/html-language-features/server/src/languageModelCache.ts index c022b8047a902..06d331bca8a22 100644 --- a/extensions/html-language-features/server/src/languageModelCache.ts +++ b/extensions/html-language-features/server/src/languageModelCache.ts @@ -18,10 +18,10 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime let cleanupInterval: NodeJS.Timer | undefined = undefined; if (cleanupIntervalTimeInSec > 0) { cleanupInterval = setInterval(() => { - let cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; - let uris = Object.keys(languageModels); - for (let uri of uris) { - let languageModelInfo = languageModels[uri]; + const cutoffTime = Date.now() - cleanupIntervalTimeInSec * 1000; + const uris = Object.keys(languageModels); + for (const uri of uris) { + const languageModelInfo = languageModels[uri]; if (languageModelInfo.cTime < cutoffTime) { delete languageModels[uri]; nModels--; @@ -32,14 +32,14 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime return { get(document: TextDocument): T { - let version = document.version; - let languageId = document.languageId; - let languageModelInfo = languageModels[document.uri]; + const version = document.version; + const languageId = document.languageId; + const languageModelInfo = languageModels[document.uri]; if (languageModelInfo && languageModelInfo.version === version && languageModelInfo.languageId === languageId) { languageModelInfo.cTime = Date.now(); return languageModelInfo.languageModel; } - let languageModel = parse(document); + const languageModel = parse(document); languageModels[document.uri] = { languageModel, version, languageId, cTime: Date.now() }; if (!languageModelInfo) { nModels++; @@ -48,8 +48,8 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime if (nModels === maxEntries) { let oldestTime = Number.MAX_VALUE; let oldestUri = null; - for (let uri in languageModels) { - let languageModelInfo = languageModels[uri]; + for (const uri in languageModels) { + const languageModelInfo = languageModels[uri]; if (languageModelInfo.cTime < oldestTime) { oldestUri = uri; oldestTime = languageModelInfo.cTime; @@ -64,7 +64,7 @@ export function getLanguageModelCache(maxEntries: number, cleanupIntervalTime }, onDocumentRemoved(document: TextDocument) { - let uri = document.uri; + const uri = document.uri; if (languageModels[uri]) { delete languageModels[uri]; nModels--; diff --git a/extensions/html-language-features/server/src/modes/cssMode.ts b/extensions/html-language-features/server/src/modes/cssMode.ts index 9f147f50cec4c..9b3ddd9b76461 100644 --- a/extensions/html-language-features/server/src/modes/cssMode.ts +++ b/extensions/html-language-features/server/src/modes/cssMode.ts @@ -4,11 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; -import { TextDocument, Position, Range, CompletionList } from 'vscode-html-languageservice'; -import { Stylesheet, FoldingRange, LanguageService as CSSLanguageService } from 'vscode-css-languageservice'; -import { LanguageMode, Workspace } from './languageModes'; +import { Stylesheet, LanguageService as CSSLanguageService } from 'vscode-css-languageservice'; +import { LanguageMode, Workspace, Color, TextDocument, Position, Range, CompletionList, DocumentContext } from './languageModes'; import { HTMLDocumentRegions, CSS_STYLE_RULE } from './embeddedSupport'; -import { Color } from 'vscode-languageserver'; export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegions: LanguageModelCache, workspace: Workspace): LanguageMode { let embeddedCSSDocuments = getLanguageModelCache(10, 60, document => documentRegions.get(document).getEmbeddedDocument('css')); @@ -18,47 +16,51 @@ export function getCSSMode(cssLanguageService: CSSLanguageService, documentRegio getId() { return 'css'; }, - doValidation(document: TextDocument, settings = workspace.settings) { + async doValidation(document: TextDocument, settings = workspace.settings) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.doValidation(embedded, cssStylesheets.get(embedded), settings && settings.css); }, - doComplete(document: TextDocument, position: Position, _settings = workspace.settings) { + async doComplete(document: TextDocument, position: Position, documentContext: DocumentContext, _settings = workspace.settings) { let embedded = embeddedCSSDocuments.get(document); const stylesheet = cssStylesheets.get(embedded); - return cssLanguageService.doComplete(embedded, position, stylesheet) || CompletionList.create(); + return cssLanguageService.doComplete2(embedded, position, stylesheet, documentContext) || CompletionList.create(); }, - doHover(document: TextDocument, position: Position) { + async doHover(document: TextDocument, position: Position) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.doHover(embedded, position, cssStylesheets.get(embedded)); }, - findDocumentHighlight(document: TextDocument, position: Position) { + async findDocumentHighlight(document: TextDocument, position: Position) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.findDocumentHighlights(embedded, position, cssStylesheets.get(embedded)); }, - findDocumentSymbols(document: TextDocument) { + async findDocumentSymbols(document: TextDocument) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.findDocumentSymbols(embedded, cssStylesheets.get(embedded)).filter(s => s.name !== CSS_STYLE_RULE); }, - findDefinition(document: TextDocument, position: Position) { + async findDefinition(document: TextDocument, position: Position) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.findDefinition(embedded, position, cssStylesheets.get(embedded)); }, - findReferences(document: TextDocument, position: Position) { + async findReferences(document: TextDocument, position: Position) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.findReferences(embedded, position, cssStylesheets.get(embedded)); }, - findDocumentColors(document: TextDocument) { + async findDocumentColors(document: TextDocument) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.findDocumentColors(embedded, cssStylesheets.get(embedded)); }, - getColorPresentations(document: TextDocument, color: Color, range: Range) { + async getColorPresentations(document: TextDocument, color: Color, range: Range) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.getColorPresentations(embedded, cssStylesheets.get(embedded), color, range); }, - getFoldingRanges(document: TextDocument): FoldingRange[] { + async getFoldingRanges(document: TextDocument) { let embedded = embeddedCSSDocuments.get(document); return cssLanguageService.getFoldingRanges(embedded, {}); }, + async getSelectionRange(document: TextDocument, position: Position) { + let embedded = embeddedCSSDocuments.get(document); + return cssLanguageService.getSelectionRanges(embedded, [position], cssStylesheets.get(embedded))[0]; + }, onDocumentRemoved(document: TextDocument) { embeddedCSSDocuments.onDocumentRemoved(document); cssStylesheets.onDocumentRemoved(document); diff --git a/extensions/html-language-features/server/src/modes/embeddedSupport.ts b/extensions/html-language-features/server/src/modes/embeddedSupport.ts index df47be3ed97d3..c3b8ffde437c4 100644 --- a/extensions/html-language-features/server/src/modes/embeddedSupport.ts +++ b/extensions/html-language-features/server/src/modes/embeddedSupport.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextDocument, Position, LanguageService, TokenType, Range } from 'vscode-html-languageservice'; +import { TextDocument, Position, LanguageService, TokenType, Range } from './languageModes'; export interface LanguageRange extends Range { languageId: string | undefined; @@ -58,6 +58,8 @@ export function getDocumentRegions(languageService: LanguageService, document: T } else if (lastAttributeName === 'type' && lastTagName.toLowerCase() === 'script') { if (/["'](module|(text|application)\/(java|ecma)script|text\/babel)["']/.test(scanner.getTokenText())) { languageIdFromType = 'javascript'; + } else if (/["']text\/typescript["']/.test(scanner.getTokenText())) { + languageIdFromType = 'typescript'; } else { languageIdFromType = undefined; } diff --git a/extensions/html-language-features/server/src/modes/formatting.ts b/extensions/html-language-features/server/src/modes/formatting.ts index a9df4b4a30ba7..49c3a0dd68ee8 100644 --- a/extensions/html-language-features/server/src/modes/formatting.ts +++ b/extensions/html-language-features/server/src/modes/formatting.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextDocument, Range, TextEdit, FormattingOptions, Position } from 'vscode-html-languageservice'; -import { LanguageModes, Settings, LanguageModeRange } from './languageModes'; +import { LanguageModes, Settings, LanguageModeRange, TextDocument, Range, TextEdit, FormattingOptions, Position } from './languageModes'; import { pushAll } from '../utils/arrays'; import { isEOL } from '../utils/strings'; -export function format(languageModes: LanguageModes, document: TextDocument, formatRange: Range, formattingOptions: FormattingOptions, settings: Settings | undefined, enabledModes: { [mode: string]: boolean }) { +export async function format(languageModes: LanguageModes, document: TextDocument, formatRange: Range, formattingOptions: FormattingOptions, settings: Settings | undefined, enabledModes: { [mode: string]: boolean }) { let result: TextEdit[] = []; let endPos = formatRange.end; @@ -40,7 +39,7 @@ export function format(languageModes: LanguageModes, document: TextDocument, for while (i < allRanges.length && !isHTML(allRanges[i])) { let range = allRanges[i]; if (!range.attributeValue && range.mode && range.mode.format) { - let edits = range.mode.format(document, Range.create(startPos, range.end), formattingOptions, settings); + let edits = await range.mode.format(document, Range.create(startPos, range.end), formattingOptions, settings); pushAll(result, edits); } startPos = range.end; @@ -54,7 +53,7 @@ export function format(languageModes: LanguageModes, document: TextDocument, for // perform a html format and apply changes to a new document let htmlMode = languageModes.getMode('html')!; - let htmlEdits = htmlMode.format!(document, formatRange, formattingOptions, settings); + let htmlEdits = await htmlMode.format!(document, formatRange, formattingOptions, settings); let htmlFormattedContent = TextDocument.applyEdits(document, htmlEdits); let newDocument = TextDocument.create(document.uri + '.tmp', document.languageId, document.version, htmlFormattedContent); try { @@ -68,7 +67,7 @@ export function format(languageModes: LanguageModes, document: TextDocument, for for (let r of embeddedRanges) { let mode = r.mode; if (mode && mode.format && enabledModes[mode.getId()] && !r.attributeValue) { - let edits = mode.format(newDocument, r, formattingOptions, settings); + let edits = await mode.format(newDocument, r, formattingOptions, settings); for (let edit of edits) { embeddedEdits.push(edit); } diff --git a/extensions/html-language-features/server/src/modes/htmlFolding.ts b/extensions/html-language-features/server/src/modes/htmlFolding.ts index aacdcc604fc41..78674fb0bc413 100644 --- a/extensions/html-language-features/server/src/modes/htmlFolding.ts +++ b/extensions/html-language-features/server/src/modes/htmlFolding.ts @@ -3,25 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextDocument, CancellationToken, Position, Range } from 'vscode-languageserver'; -import { FoldingRange } from 'vscode-html-languageservice'; -import { LanguageModes, LanguageMode } from './languageModes'; +import { TextDocument, FoldingRange, Position, Range, LanguageModes, LanguageMode } from './languageModes'; +import { CancellationToken } from 'vscode-languageserver'; -export function getFoldingRanges(languageModes: LanguageModes, document: TextDocument, maxRanges: number | undefined, _cancellationToken: CancellationToken | null): FoldingRange[] { +export async function getFoldingRanges(languageModes: LanguageModes, document: TextDocument, maxRanges: number | undefined, _cancellationToken: CancellationToken | null): Promise { let htmlMode = languageModes.getMode('html'); let range = Range.create(Position.create(0, 0), Position.create(document.lineCount, 0)); let result: FoldingRange[] = []; if (htmlMode && htmlMode.getFoldingRanges) { - result.push(...htmlMode.getFoldingRanges(document)); + result.push(... await htmlMode.getFoldingRanges(document)); } // cache folding ranges per mode let rangesPerMode: { [mode: string]: FoldingRange[] } = Object.create(null); - let getRangesForMode = (mode: LanguageMode) => { + let getRangesForMode = async (mode: LanguageMode) => { if (mode.getFoldingRanges) { let ranges = rangesPerMode[mode.getId()]; if (!Array.isArray(ranges)) { - ranges = mode.getFoldingRanges(document) || []; + ranges = await mode.getFoldingRanges(document) || []; rangesPerMode[mode.getId()] = ranges; } return ranges; @@ -33,7 +32,7 @@ export function getFoldingRanges(languageModes: LanguageModes, document: TextDoc for (let modeRange of modeRanges) { let mode = modeRange.mode; if (mode && mode !== htmlMode && !modeRange.attributeValue) { - const ranges = getRangesForMode(mode); + const ranges = await getRangesForMode(mode); result.push(...ranges.filter(r => r.startLine >= modeRange.start.line && r.endLine < modeRange.end.line)); } } diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 4449b01bd7cb6..96462f8e011d5 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -7,10 +7,9 @@ import { getLanguageModelCache } from '../languageModelCache'; import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration, SelectionRange, - TextDocument, Position, Range, CompletionItem, FoldingRange -} from 'vscode-html-languageservice'; -import { LanguageMode, Workspace } from './languageModes'; -import { getPathCompletionParticipant } from './pathCompletion'; + TextDocument, Position, Range, FoldingRange, + LanguageMode, Workspace +} from './languageModes'; export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: Workspace): LanguageMode { let htmlDocuments = getLanguageModelCache(10, 60, document => htmlLanguageService.parseHTMLDocument(document)); @@ -18,37 +17,33 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: getId() { return 'html'; }, - getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[] { - return htmlLanguageService.getSelectionRanges(document, positions); + async getSelectionRange(document: TextDocument, position: Position): Promise { + return htmlLanguageService.getSelectionRanges(document, [position])[0]; }, - doComplete(document: TextDocument, position: Position, settings = workspace.settings) { + doComplete(document: TextDocument, position: Position, documentContext: DocumentContext, settings = workspace.settings) { let options = settings && settings.html && settings.html.suggest; let doAutoComplete = settings && settings.html && settings.html.autoClosingTags; if (doAutoComplete) { options.hideAutoCompleteProposals = true; } - let pathCompletionProposals: CompletionItem[] = []; - let participants = [getPathCompletionParticipant(document, workspace.folders, pathCompletionProposals)]; - htmlLanguageService.setCompletionParticipants(participants); const htmlDocument = htmlDocuments.get(document); - let completionList = htmlLanguageService.doComplete(document, position, htmlDocument, options); - completionList.items.push(...pathCompletionProposals); + let completionList = htmlLanguageService.doComplete2(document, position, htmlDocument, documentContext, options); return completionList; }, - doHover(document: TextDocument, position: Position) { + async doHover(document: TextDocument, position: Position) { return htmlLanguageService.doHover(document, position, htmlDocuments.get(document)); }, - findDocumentHighlight(document: TextDocument, position: Position) { + async findDocumentHighlight(document: TextDocument, position: Position) { return htmlLanguageService.findDocumentHighlights(document, position, htmlDocuments.get(document)); }, - findDocumentLinks(document: TextDocument, documentContext: DocumentContext) { + async findDocumentLinks(document: TextDocument, documentContext: DocumentContext) { return htmlLanguageService.findDocumentLinks(document, documentContext); }, - findDocumentSymbols(document: TextDocument) { + async findDocumentSymbols(document: TextDocument) { return htmlLanguageService.findDocumentSymbols(document, htmlDocuments.get(document)); }, - format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings = workspace.settings) { + async format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings = workspace.settings) { let formatSettings: HTMLFormatConfiguration = settings && settings.html && settings.html.format; if (formatSettings) { formatSettings = merge(formatSettings, {}); @@ -63,10 +58,10 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: formatSettings = merge(formatParams, formatSettings); return htmlLanguageService.format(document, range, formatSettings); }, - getFoldingRanges(document: TextDocument): FoldingRange[] { + async getFoldingRanges(document: TextDocument): Promise { return htmlLanguageService.getFoldingRanges(document); }, - doAutoClose(document: TextDocument, position: Position) { + async doAutoClose(document: TextDocument, position: Position) { let offset = document.offsetAt(position); let text = document.getText(); if (offset > 0 && text.charAt(offset - 1).match(/[>\/]/g)) { @@ -74,9 +69,21 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: } return null; }, - onDocumentRemoved(document: TextDocument) { + async doRename(document: TextDocument, position: Position, newName: string) { + const htmlDocument = htmlDocuments.get(document); + return htmlLanguageService.doRename(document, position, newName, htmlDocument); + }, + async onDocumentRemoved(document: TextDocument) { htmlDocuments.onDocumentRemoved(document); }, + async findMatchingTagPosition(document: TextDocument, position: Position) { + const htmlDocument = htmlDocuments.get(document); + return htmlLanguageService.findMatchingTagPosition(document, position, htmlDocument); + }, + async doOnTypeRename(document: TextDocument, position: Position) { + const htmlDocument = htmlDocuments.get(document); + return htmlLanguageService.findOnTypeRenameRanges(document, position, htmlDocument); + }, dispose() { htmlDocuments.dispose(); } diff --git a/extensions/html-language-features/server/src/modes/javascriptLibs.ts b/extensions/html-language-features/server/src/modes/javascriptLibs.ts new file mode 100644 index 0000000000000..bdca89be362c1 --- /dev/null +++ b/extensions/html-language-features/server/src/modes/javascriptLibs.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { join, basename, dirname } from 'path'; +import { readFileSync } from 'fs'; + +const contents: { [name: string]: string } = {}; + +const serverFolder = basename(__dirname) === 'dist' ? dirname(__dirname) : dirname(dirname(__dirname)); +const TYPESCRIPT_LIB_SOURCE = join(serverFolder, '../../node_modules/typescript/lib'); +const JQUERY_PATH = join(serverFolder, 'lib/jquery.d.ts'); + +export function loadLibrary(name: string) { + let content = contents[name]; + if (typeof content !== 'string') { + let libPath; + if (name === 'jquery') { + libPath = JQUERY_PATH; + } else { + libPath = join(TYPESCRIPT_LIB_SOURCE, name); // from source + } + try { + content = readFileSync(libPath).toString(); + } catch (e) { + console.log(`Unable to load library ${name} at ${libPath}: ${e.message}`); + content = ''; + } + contents[name] = content; + } + return content; +} diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index 302b3dc271a83..086b8a906b81e 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -7,92 +7,106 @@ import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache import { SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, - DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions, FoldingRange, FoldingRangeKind -} from 'vscode-html-languageservice'; -import { LanguageMode, Settings } from './languageModes'; -import { getWordAtText, startsWith, isWhitespaceOnly, repeat } from '../utils/strings'; + DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions, FoldingRange, FoldingRangeKind, SelectionRange, + LanguageMode, Settings, SemanticTokenData, Workspace, DocumentContext +} from './languageModes'; +import { getWordAtText, isWhitespaceOnly, repeat } from '../utils/strings'; import { HTMLDocumentRegions } from './embeddedSupport'; import * as ts from 'typescript'; -import { join } from 'path'; +import { getSemanticTokens, getSemanticTokenLegend } from './javascriptSemanticTokens'; -const FILE_NAME = 'vscode://javascript/1'; // the same 'file' is used for all contents const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g; -let jquery_d_ts = join(__dirname, '../lib/jquery.d.ts'); // when packaged -if (!ts.sys.fileExists(jquery_d_ts)) { - jquery_d_ts = join(__dirname, '../../lib/jquery.d.ts'); // from source -} - -export function getJavaScriptMode(documentRegions: LanguageModelCache): LanguageMode { - let jsDocuments = getLanguageModelCache(10, 60, document => documentRegions.get(document).getEmbeddedDocument('javascript')); +function getLanguageServiceHost(scriptKind: ts.ScriptKind) { + const compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false }; - let compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es6.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic }; - let currentTextDocument: TextDocument; - let scriptFileVersion: number = 0; - function updateCurrentTextDocument(doc: TextDocument) { - if (!currentTextDocument || doc.uri !== currentTextDocument.uri || doc.version !== currentTextDocument.version) { - currentTextDocument = jsDocuments.get(doc); - scriptFileVersion++; - } - } - const host: ts.LanguageServiceHost = { - getCompilationSettings: () => compilerOptions, - getScriptFileNames: () => [FILE_NAME, jquery_d_ts], - getScriptKind: () => ts.ScriptKind.JS, - getScriptVersion: (fileName: string) => { - if (fileName === FILE_NAME) { - return String(scriptFileVersion); - } - return '1'; // default lib an jquery.d.ts are static - }, - getScriptSnapshot: (fileName: string) => { - let text = ''; - if (startsWith(fileName, 'vscode:')) { - if (fileName === FILE_NAME) { + let currentTextDocument = TextDocument.create('init', 'javascript', 1, ''); + const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs').then(libs => { + const host: ts.LanguageServiceHost = { + getCompilationSettings: () => compilerOptions, + getScriptFileNames: () => [currentTextDocument.uri, 'jquery'], + getScriptKind: (fileName) => { + if (fileName === currentTextDocument.uri) { + return scriptKind; + } + return fileName.substr(fileName.length - 2) === 'ts' ? ts.ScriptKind.TS : ts.ScriptKind.JS; + }, + getScriptVersion: (fileName: string) => { + if (fileName === currentTextDocument.uri) { + return String(currentTextDocument.version); + } + return '1'; // default lib an jquery.d.ts are static + }, + getScriptSnapshot: (fileName: string) => { + let text = ''; + if (fileName === currentTextDocument.uri) { text = currentTextDocument.getText(); + } else { + text = libs.loadLibrary(fileName); } - } else { - text = ts.sys.readFile(fileName) || ''; - } - return { - getText: (start, end) => text.substring(start, end), - getLength: () => text.length, - getChangeRange: () => undefined - }; + return { + getText: (start, end) => text.substring(start, end), + getLength: () => text.length, + getChangeRange: () => undefined + }; + }, + getCurrentDirectory: () => '', + getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6' + }; + return ts.createLanguageService(host); + }); + return { + async getLanguageService(jsDocument: TextDocument): Promise { + currentTextDocument = jsDocument; + return jsLanguageService; }, - getCurrentDirectory: () => '', - getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options) + getCompilationSettings() { + return compilerOptions; + }, + dispose() { + if (jsLanguageService) { + jsLanguageService.then(s => s.dispose()); + } + } }; - let jsLanguageService = ts.createLanguageService(host); +} + + +export function getJavaScriptMode(documentRegions: LanguageModelCache, languageId: 'javascript' | 'typescript', workspace: Workspace): LanguageMode { + let jsDocuments = getLanguageModelCache(10, 60, document => documentRegions.get(document).getEmbeddedDocument(languageId)); + const host = getLanguageServiceHost(languageId === 'javascript' ? ts.ScriptKind.JS : ts.ScriptKind.TS); let globalSettings: Settings = {}; return { getId() { - return 'javascript'; + return languageId; }, - doValidation(document: TextDocument): Diagnostic[] { - updateCurrentTextDocument(document); - const syntaxDiagnostics: ts.Diagnostic[] = jsLanguageService.getSyntacticDiagnostics(FILE_NAME); - const semanticDiagnostics = jsLanguageService.getSemanticDiagnostics(FILE_NAME); + async doValidation(document: TextDocument, settings = workspace.settings): Promise { + host.getCompilationSettings()['experimentalDecorators'] = settings && settings.javascript && settings.javascript.implicitProjectConfig.experimentalDecorators; + const jsDocument = jsDocuments.get(document); + const languageService = await host.getLanguageService(jsDocument); + const syntaxDiagnostics: ts.Diagnostic[] = languageService.getSyntacticDiagnostics(jsDocument.uri); + const semanticDiagnostics = languageService.getSemanticDiagnostics(jsDocument.uri); return syntaxDiagnostics.concat(semanticDiagnostics).map((diag: ts.Diagnostic): Diagnostic => { return { - range: convertRange(currentTextDocument, diag), + range: convertRange(jsDocument, diag), severity: DiagnosticSeverity.Error, - source: 'js', + source: languageId, message: ts.flattenDiagnosticMessageText(diag.messageText, '\n') }; }); }, - doComplete(document: TextDocument, position: Position): CompletionList { - updateCurrentTextDocument(document); - let offset = currentTextDocument.offsetAt(position); - let completions = jsLanguageService.getCompletionsAtPosition(FILE_NAME, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + async doComplete(document: TextDocument, position: Position, _documentContext: DocumentContext): Promise { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let offset = jsDocument.offsetAt(position); + let completions = jsLanguageService.getCompletionsAtPosition(jsDocument.uri, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); if (!completions) { return { isIncomplete: false, items: [] }; } - let replaceRange = convertRange(currentTextDocument, getWordAtText(currentTextDocument.getText(), offset, JS_WORD_REGEX)); + let replaceRange = convertRange(jsDocument, getWordAtText(jsDocument.getText(), offset, JS_WORD_REGEX)); return { isIncomplete: false, items: completions.entries.map(entry => { @@ -104,7 +118,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined); if (details) { item.detail = ts.displayPartsToString(details.displayParts); item.documentation = ts.displayPartsToString(details.documentation); @@ -122,21 +137,23 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let info = jsLanguageService.getQuickInfoAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); if (info) { let contents = ts.displayPartsToString(info.displayParts); return { - range: convertRange(currentTextDocument, info.textSpan), + range: convertRange(jsDocument, info.textSpan), contents: MarkedString.fromPlainText(contents) }; } return null; }, - doSignatureHelp(document: TextDocument, position: Position): SignatureHelp | null { - updateCurrentTextDocument(document); - let signHelp = jsLanguageService.getSignatureHelpItems(FILE_NAME, currentTextDocument.offsetAt(position), undefined); + async doSignatureHelp(document: TextDocument, position: Position): Promise { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let signHelp = jsLanguageService.getSignatureHelpItems(jsDocument.uri, jsDocument.offsetAt(position), undefined); if (signHelp) { let ret: SignatureHelp = { activeSignature: signHelp.selectedItemIndex, @@ -171,23 +188,25 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + const highlights = jsLanguageService.getDocumentHighlights(jsDocument.uri, jsDocument.offsetAt(position), [jsDocument.uri]); const out: DocumentHighlight[] = []; for (const entry of highlights || []) { for (const highlight of entry.highlightSpans) { out.push({ - range: convertRange(currentTextDocument, highlight.textSpan), + range: convertRange(jsDocument, highlight.textSpan), kind: highlight.kind === 'writtenReference' ? DocumentHighlightKind.Write : DocumentHighlightKind.Text }); } } return out; }, - findDocumentSymbols(document: TextDocument): SymbolInformation[] { - updateCurrentTextDocument(document); - let items = jsLanguageService.getNavigationBarItems(FILE_NAME); + async findDocumentSymbols(document: TextDocument): Promise { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let items = jsLanguageService.getNavigationBarItems(jsDocument.uri); if (items) { let result: SymbolInformation[] = []; let existing = Object.create(null); @@ -199,7 +218,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let definition = jsLanguageService.getDefinitionAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); if (definition) { - return definition.filter(d => d.fileName === FILE_NAME).map(d => { + return definition.filter(d => d.fileName === jsDocument.uri).map(d => { return { uri: document.uri, - range: convertRange(currentTextDocument, d.textSpan) + range: convertRange(jsDocument, d.textSpan) }; }); } return null; }, - findReferences(document: TextDocument, position: Position): Location[] { - updateCurrentTextDocument(document); - let references = jsLanguageService.getReferencesAtPosition(FILE_NAME, currentTextDocument.offsetAt(position)); + async findReferences(document: TextDocument, position: Position): Promise { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let references = jsLanguageService.getReferencesAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); if (references) { - return references.filter(d => d.fileName === FILE_NAME).map(d => { + return references.filter(d => d.fileName === jsDocument.uri).map(d => { return { uri: document.uri, - range: convertRange(currentTextDocument, d.textSpan) + range: convertRange(jsDocument, d.textSpan) }; }); } return []; }, - format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings): TextEdit[] { - currentTextDocument = documentRegions.get(document).getEmbeddedDocument('javascript', true); - scriptFileVersion++; + async getSelectionRange(document: TextDocument, position: Position): Promise { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + function convertSelectionRange(selectionRange: ts.SelectionRange): SelectionRange { + const parent = selectionRange.parent ? convertSelectionRange(selectionRange.parent) : undefined; + return SelectionRange.create(convertRange(jsDocument, selectionRange.textSpan), parent); + } + const range = jsLanguageService.getSmartSelectionRange(jsDocument.uri, jsDocument.offsetAt(position)); + return convertSelectionRange(range); + }, + async format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings): Promise { + const jsDocument = documentRegions.get(document).getEmbeddedDocument('javascript', true); + const jsLanguageService = await host.getLanguageService(jsDocument); let formatterSettings = settings && settings.javascript && settings.javascript.format; let initialIndentLevel = computeInitialIndent(document, range, formatParams); let formatSettings = convertOptions(formatParams, formatterSettings, initialIndentLevel + 1); - let start = currentTextDocument.offsetAt(range.start); - let end = currentTextDocument.offsetAt(range.end); + let start = jsDocument.offsetAt(range.start); + let end = jsDocument.offsetAt(range.end); let lastLineRange = null; - if (range.end.line > range.start.line && (range.end.character === 0 || isWhitespaceOnly(currentTextDocument.getText().substr(end - range.end.character, range.end.character)))) { + if (range.end.line > range.start.line && (range.end.character === 0 || isWhitespaceOnly(jsDocument.getText().substr(end - range.end.character, range.end.character)))) { end -= range.end.character; lastLineRange = Range.create(Position.create(range.end.line, 0), range.end); } - let edits = jsLanguageService.getFormattingEditsForRange(FILE_NAME, start, end, formatSettings); + let edits = jsLanguageService.getFormattingEditsForRange(jsDocument.uri, start, end, formatSettings); if (edits) { let result = []; for (let edit of edits) { if (edit.span.start >= start && edit.span.start + edit.span.length <= end) { result.push({ - range: convertRange(currentTextDocument, edit.span), + range: convertRange(jsDocument, edit.span), newText: edit.newText }); } @@ -283,12 +314,13 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + let spans = jsLanguageService.getOutliningSpans(jsDocument.uri); let ranges: FoldingRange[] = []; for (let span of spans) { - let curr = convertRange(currentTextDocument, span.textSpan); + let curr = convertRange(jsDocument, span.textSpan); let startLine = curr.start.line; let endLine = curr.end.line; if (startLine < endLine) { @@ -305,13 +337,24 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument); + return getSemanticTokens(jsLanguageService, jsDocument, jsDocument.uri); + }, + getSemanticTokenLegend(): { types: string[], modifiers: string[] } { + return getSemanticTokenLegend(); + }, dispose() { - jsLanguageService.dispose(); + host.dispose(); jsDocuments.dispose(); } }; } + + + function convertRange(document: TextDocument, span: { start: number | undefined, length: number | undefined }): Range { if (typeof span.start === 'undefined') { const pos = document.positionAt(0); diff --git a/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts new file mode 100644 index 0000000000000..2f91c3d148646 --- /dev/null +++ b/extensions/html-language-features/server/src/modes/javascriptSemanticTokens.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TextDocument, SemanticTokenData } from './languageModes'; +import * as ts from 'typescript'; + +export function getSemanticTokenLegend() { + if (tokenTypes.length !== TokenType._) { + console.warn('TokenType has added new entries.'); + } + if (tokenModifiers.length !== TokenModifier._) { + console.warn('TokenModifier has added new entries.'); + } + return { types: tokenTypes, modifiers: tokenModifiers }; +} + +export function getSemanticTokens(jsLanguageService: ts.LanguageService, currentTextDocument: TextDocument, fileName: string): SemanticTokenData[] { + //https://ts-ast-viewer.com/#code/AQ0g2CmAuwGbALzAJwG4BQZQGNwEMBnQ4AQQEYBmYAb2C22zgEtJwATJVTRxgcwD27AQAp8AGmAAjAJS0A9POB8+7NQ168oscAJz5wANXwAnLug2bsJmAFcTAO2XAA1MHyvgu-UdOeWbOw8ViAAvpagocBAA + + let resultTokens: SemanticTokenData[] = []; + const collector = (node: ts.Node, typeIdx: number, modifierSet: number) => { + resultTokens.push({ start: currentTextDocument.positionAt(node.getStart()), length: node.getWidth(), typeIdx, modifierSet }); + }; + collectTokens(jsLanguageService, fileName, { start: 0, length: currentTextDocument.getText().length }, collector); + + return resultTokens; +} + +function collectTokens(jsLanguageService: ts.LanguageService, fileName: string, span: ts.TextSpan, collector: (node: ts.Node, tokenType: number, tokenModifier: number) => void) { + + const program = jsLanguageService.getProgram(); + if (program) { + const typeChecker = program.getTypeChecker(); + + function visit(node: ts.Node) { + if (!node || !ts.textSpanIntersectsWith(span, node.pos, node.getFullWidth())) { + return; + } + if (ts.isIdentifier(node)) { + let symbol = typeChecker.getSymbolAtLocation(node); + if (symbol) { + if (symbol.flags & ts.SymbolFlags.Alias) { + symbol = typeChecker.getAliasedSymbol(symbol); + } + let typeIdx = classifySymbol(symbol); + if (typeIdx !== undefined) { + let modifierSet = 0; + if (node.parent) { + const parentTypeIdx = tokenFromDeclarationMapping[node.parent.kind]; + if (parentTypeIdx === typeIdx && (node.parent).name === node) { + modifierSet = 1 << TokenModifier.declaration; + } + } + const decl = symbol.valueDeclaration; + const modifiers = decl ? ts.getCombinedModifierFlags(decl) : 0; + const nodeFlags = decl ? ts.getCombinedNodeFlags(decl) : 0; + if (modifiers & ts.ModifierFlags.Static) { + modifierSet |= 1 << TokenModifier.static; + } + if (modifiers & ts.ModifierFlags.Async) { + modifierSet |= 1 << TokenModifier.async; + } + if ((modifiers & ts.ModifierFlags.Readonly) || (nodeFlags & ts.NodeFlags.Const) || (symbol.getFlags() & ts.SymbolFlags.EnumMember)) { + modifierSet |= 1 << TokenModifier.readonly; + } + collector(node, typeIdx, modifierSet); + } + } + } + + ts.forEachChild(node, visit); + } + const sourceFile = program.getSourceFile(fileName); + if (sourceFile) { + visit(sourceFile); + } + } +} + +function classifySymbol(symbol: ts.Symbol) { + const flags = symbol.getFlags(); + if (flags & ts.SymbolFlags.Class) { + return TokenType.class; + } else if (flags & ts.SymbolFlags.Enum) { + return TokenType.enum; + } else if (flags & ts.SymbolFlags.TypeAlias) { + return TokenType.type; + } else if (flags & ts.SymbolFlags.Type) { + if (flags & ts.SymbolFlags.Interface) { + return TokenType.interface; + } if (flags & ts.SymbolFlags.TypeParameter) { + return TokenType.typeParameter; + } + } + const decl = symbol.valueDeclaration || symbol.declarations && symbol.declarations[0]; + return decl && tokenFromDeclarationMapping[decl.kind]; +} + +export const enum TokenType { + class, enum, interface, namespace, typeParameter, type, parameter, variable, property, function, member, _ +} + +export const enum TokenModifier { + declaration, static, async, readonly, _ +} + +const tokenTypes: string[] = []; +tokenTypes[TokenType.class] = 'class'; +tokenTypes[TokenType.enum] = 'enum'; +tokenTypes[TokenType.interface] = 'interface'; +tokenTypes[TokenType.namespace] = 'namespace'; +tokenTypes[TokenType.typeParameter] = 'typeParameter'; +tokenTypes[TokenType.type] = 'type'; +tokenTypes[TokenType.parameter] = 'parameter'; +tokenTypes[TokenType.variable] = 'variable'; +tokenTypes[TokenType.property] = 'property'; +tokenTypes[TokenType.function] = 'function'; +tokenTypes[TokenType.member] = 'member'; + +const tokenModifiers: string[] = []; +tokenModifiers[TokenModifier.async] = 'async'; +tokenModifiers[TokenModifier.declaration] = 'declaration'; +tokenModifiers[TokenModifier.readonly] = 'readonly'; +tokenModifiers[TokenModifier.static] = 'static'; + +const tokenFromDeclarationMapping: { [name: string]: TokenType } = { + [ts.SyntaxKind.VariableDeclaration]: TokenType.variable, + [ts.SyntaxKind.Parameter]: TokenType.parameter, + [ts.SyntaxKind.PropertyDeclaration]: TokenType.property, + [ts.SyntaxKind.ModuleDeclaration]: TokenType.namespace, + [ts.SyntaxKind.EnumDeclaration]: TokenType.enum, + [ts.SyntaxKind.EnumMember]: TokenType.property, + [ts.SyntaxKind.ClassDeclaration]: TokenType.class, + [ts.SyntaxKind.MethodDeclaration]: TokenType.member, + [ts.SyntaxKind.FunctionDeclaration]: TokenType.function, + [ts.SyntaxKind.MethodSignature]: TokenType.member, + [ts.SyntaxKind.GetAccessor]: TokenType.property, + [ts.SyntaxKind.PropertySignature]: TokenType.property, + [ts.SyntaxKind.InterfaceDeclaration]: TokenType.interface, + [ts.SyntaxKind.TypeAliasDeclaration]: TokenType.type, + [ts.SyntaxKind.TypeParameter]: TokenType.typeParameter +}; diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 0a8e53a4ef36d..e47df5a797bd1 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -8,7 +8,7 @@ import { ClientCapabilities, DocumentContext, getLanguageService as getHTMLLanguageService, IHTMLDataProvider, SelectionRange, CompletionItem, CompletionList, Definition, Diagnostic, DocumentHighlight, DocumentLink, FoldingRange, FormattingOptions, Hover, Location, Position, Range, SignatureHelp, SymbolInformation, TextDocument, TextEdit, - Color, ColorInformation, ColorPresentation + Color, ColorInformation, ColorPresentation, WorkspaceEdit } from 'vscode-html-languageservice'; import { WorkspaceFolder } from 'vscode-languageserver'; import { getLanguageModelCache, LanguageModelCache } from '../languageModelCache'; @@ -16,8 +16,10 @@ import { getCSSMode } from './cssMode'; import { getDocumentRegions, HTMLDocumentRegions } from './embeddedSupport'; import { getHTMLMode } from './htmlMode'; import { getJavaScriptMode } from './javascriptMode'; +import { RequestService } from '../requests'; -export { ColorInformation, ColorPresentation, Color }; +export * from 'vscode-html-languageservice'; +export { WorkspaceFolder } from 'vscode-languageserver'; export interface Settings { css?: any; @@ -30,29 +32,42 @@ export interface Workspace { readonly folders: WorkspaceFolder[]; } +export interface SemanticTokenData { + start: Position; + length: number; + typeIdx: number; + modifierSet: number; +} + export interface LanguageMode { getId(): string; - getSelectionRanges?: (document: TextDocument, positions: Position[]) => SelectionRange[]; - doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[]; - doComplete?: (document: TextDocument, position: Position, settings?: Settings) => CompletionList; - doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem; - doHover?: (document: TextDocument, position: Position) => Hover | null; - doSignatureHelp?: (document: TextDocument, position: Position) => SignatureHelp | null; - findDocumentHighlight?: (document: TextDocument, position: Position) => DocumentHighlight[]; - findDocumentSymbols?: (document: TextDocument) => SymbolInformation[]; - findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => DocumentLink[]; - findDefinition?: (document: TextDocument, position: Position) => Definition | null; - findReferences?: (document: TextDocument, position: Position) => Location[]; - format?: (document: TextDocument, range: Range, options: FormattingOptions, settings?: Settings) => TextEdit[]; - findDocumentColors?: (document: TextDocument) => ColorInformation[]; - getColorPresentations?: (document: TextDocument, color: Color, range: Range) => ColorPresentation[]; - doAutoClose?: (document: TextDocument, position: Position) => string | null; - getFoldingRanges?: (document: TextDocument) => FoldingRange[]; + getSelectionRange?: (document: TextDocument, position: Position) => Promise; + doValidation?: (document: TextDocument, settings?: Settings) => Promise; + doComplete?: (document: TextDocument, position: Position, documentContext: DocumentContext, settings?: Settings) => Promise; + doResolve?: (document: TextDocument, item: CompletionItem) => Promise; + doHover?: (document: TextDocument, position: Position) => Promise; + doSignatureHelp?: (document: TextDocument, position: Position) => Promise; + doRename?: (document: TextDocument, position: Position, newName: string) => Promise; + doOnTypeRename?: (document: TextDocument, position: Position) => Promise; + findDocumentHighlight?: (document: TextDocument, position: Position) => Promise; + findDocumentSymbols?: (document: TextDocument) => Promise; + findDocumentLinks?: (document: TextDocument, documentContext: DocumentContext) => Promise; + findDefinition?: (document: TextDocument, position: Position) => Promise; + findReferences?: (document: TextDocument, position: Position) => Promise; + format?: (document: TextDocument, range: Range, options: FormattingOptions, settings?: Settings) => Promise; + findDocumentColors?: (document: TextDocument) => Promise; + getColorPresentations?: (document: TextDocument, color: Color, range: Range) => Promise; + doAutoClose?: (document: TextDocument, position: Position) => Promise; + findMatchingTagPosition?: (document: TextDocument, position: Position) => Promise; + getFoldingRanges?: (document: TextDocument) => Promise; onDocumentRemoved(document: TextDocument): void; + getSemanticTokens?(document: TextDocument): Promise; + getSemanticTokenLegend?(): { types: string[], modifiers: string[] }; dispose(): void; } export interface LanguageModes { + updateDataProviders(dataProviders: IHTMLDataProvider[]): void; getModeAtPosition(document: TextDocument, position: Position): LanguageMode | undefined; getModesInRange(document: TextDocument, range: Range): LanguageModeRange[]; getAllModes(): LanguageMode[]; @@ -67,9 +82,9 @@ export interface LanguageModeRange extends Range { attributeValue?: boolean; } -export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, clientCapabilities: ClientCapabilities, customDataProviders?: IHTMLDataProvider[]): LanguageModes { - const htmlLanguageService = getHTMLLanguageService({ customDataProviders, clientCapabilities }); - const cssLanguageService = getCSSLanguageService({ clientCapabilities }); +export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, clientCapabilities: ClientCapabilities, requestService: RequestService): LanguageModes { + const htmlLanguageService = getHTMLLanguageService({ clientCapabilities, fileSystemProvider: requestService }); + const cssLanguageService = getCSSLanguageService({ clientCapabilities, fileSystemProvider: requestService }); let documentRegions = getLanguageModelCache(10, 60, document => getDocumentRegions(htmlLanguageService, document)); @@ -82,9 +97,13 @@ export function getLanguageModes(supportedLanguages: { [languageId: string]: boo modes['css'] = getCSSMode(cssLanguageService, documentRegions, workspace); } if (supportedLanguages['javascript']) { - modes['javascript'] = getJavaScriptMode(documentRegions); + modes['javascript'] = getJavaScriptMode(documentRegions, 'javascript', workspace); + modes['typescript'] = getJavaScriptMode(documentRegions, 'typescript', workspace); } return { + async updateDataProviders(dataProviders: IHTMLDataProvider[]): Promise { + htmlLanguageService.setDataProviders(true, dataProviders); + }, getModeAtPosition(document: TextDocument, position: Position): LanguageMode | undefined { let languageId = documentRegions.get(document).getLanguageAtPosition(position); if (languageId) { diff --git a/extensions/html-language-features/server/src/modes/pathCompletion.ts b/extensions/html-language-features/server/src/modes/pathCompletion.ts deleted file mode 100644 index 278d967bd954f..0000000000000 --- a/extensions/html-language-features/server/src/modes/pathCompletion.ts +++ /dev/null @@ -1,184 +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 { WorkspaceFolder } from 'vscode-languageserver'; -import * as path from 'path'; -import * as fs from 'fs'; -import { URI } from 'vscode-uri'; -import { ICompletionParticipant, TextDocument, CompletionItemKind, CompletionItem, TextEdit, Range, Position } from 'vscode-html-languageservice'; -import { startsWith } from '../utils/strings'; -import { contains } from '../utils/arrays'; - -export function getPathCompletionParticipant( - document: TextDocument, - workspaceFolders: WorkspaceFolder[], - result: CompletionItem[] -): ICompletionParticipant { - return { - onHtmlAttributeValue: ({ tag, attribute, value: valueBeforeCursor, range }) => { - const fullValue = stripQuotes(document.getText(range)); - - if (shouldDoPathCompletion(tag, attribute, fullValue)) { - if (workspaceFolders.length === 0) { - return; - } - const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders); - - const paths = providePaths(valueBeforeCursor, URI.parse(document.uri).fsPath, workspaceRoot); - result.push(...paths.map(p => pathToSuggestion(p, valueBeforeCursor, fullValue, range))); - } - } - }; -} - -function stripQuotes(fullValue: string) { - if (startsWith(fullValue, `'`) || startsWith(fullValue, `"`)) { - return fullValue.slice(1, -1); - } else { - return fullValue; - } -} - -function shouldDoPathCompletion(tag: string, attr: string, value: string) { - if (startsWith(value, 'http') || startsWith(value, 'https') || startsWith(value, '//')) { - return false; - } - - if (PATH_TAG_AND_ATTR[tag]) { - if (typeof PATH_TAG_AND_ATTR[tag] === 'string') { - return PATH_TAG_AND_ATTR[tag] === attr; - } else { - return contains(PATH_TAG_AND_ATTR[tag], attr); - } - } - - return false; -} - -/** - * 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 { - const paths = fs.readdirSync(parentDir).map(f => { - return isDir(path.resolve(parentDir, f)) - ? f + '/' - : f; - }); - return paths.filter(p => p[0] !== '.'); - } catch (e) { - return []; - } -} - -function isDir(p: string) { - try { - return fs.statSync(p).isDirectory(); - } catch (e) { - return false; - } -} - -function pathToSuggestion(p: string, valueBeforeCursor: string, fullValue: string, range: Range): CompletionItem { - const isDir = p[p.length - 1] === '/'; - - let replaceRange: Range; - const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/'); - if (lastIndexOfSlash === -1) { - replaceRange = shiftRange(range, 1, -1); - } 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'); }); }); @@ -99,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: { @@ -92,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( [ '', '', @@ -159,7 +159,7 @@ suite('HTML Embedded Formatting', () => { ].join('\n') ); }); - test('#58435', () => { + test('#58435', async () => { let options = { html: { format: { @@ -168,7 +168,7 @@ suite('HTML Embedded Formatting', () => { } }; - var content = [ + const content = [ '', '', '', @@ -179,7 +179,7 @@ suite('HTML Embedded Formatting', () => { '', ].join('\n'); - var expected = [ + const expected = [ '', '', '', @@ -190,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 new file mode 100644 index 0000000000000..9f9937bcaf73e --- /dev/null +++ b/extensions/html-language-features/server/src/test/selectionRanges.test.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { getLanguageModes, ClientCapabilities, TextDocument, SelectionRange} from '../modes/languageModes'; +import { getSelectionRanges } from '../modes/selectionRanges'; +import { getNodeFSRequestService } from '../node/nodeFs'; + +async function assertRanges(content: string, expected: (number | string)[][]): Promise { + let message = `${content} gives selection range:\n`; + + const offset = content.indexOf('|'); + content = content.substr(0, offset) + content.substr(offset + 1); + + let workspace = { + settings: {}, + folders: [{ name: 'foo', uri: 'test://foo' }] + }; + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); + + const document = TextDocument.create('test://foo.html', 'html', 1, content); + const actualRanges = await getSelectionRanges(languageModes, document, [document.positionAt(offset)]); + assert.equal(actualRanges.length, 1); + const offsetPairs: [number, string][] = []; + let curr: SelectionRange | undefined = actualRanges[0]; + while (curr) { + offsetPairs.push([document.offsetAt(curr.range.start), document.getText(curr.range)]); + curr = curr.parent; + } + + message += `${JSON.stringify(offsetPairs)}\n but should give:\n${JSON.stringify(expected)}\n`; + assert.deepEqual(offsetPairs, expected, message); +} + +suite('HTML SelectionRange', () => { + test('Embedded JavaScript', async () => { + await assertRanges('', [ + [48, '1'], + [48, '1+2'], + [47, '(1+2)'], + [47, '(1+2)*6'], + [46, '((1+2)*6)'], + [39, 'return ((1+2)*6)'], + [22, 'function foo() { return ((1+2)*6) }'], + [20, ' function foo() { return ((1+2)*6) }'], + [12, ''], + [6, ''], + [0, ''], + ]); + }); + + 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, ''], + [6, ''], + [0, ''], + ]); + }); + + test('Embedded style', async () => { + await assertRanges('
', [ + [19, 'red'], + [12, 'color: red'], + [11, '"color: red"'], + [5, 'style="color: red"'], + [1, 'div style="color: red"'], + [0, '
'] + ]); + }); + + +}); diff --git a/extensions/html-language-features/server/src/test/semanticTokens.test.ts b/extensions/html-language-features/server/src/test/semanticTokens.test.ts new file mode 100644 index 0000000000000..6d815748e5e20 --- /dev/null +++ b/extensions/html-language-features/server/src/test/semanticTokens.test.ts @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TextDocument, getLanguageModes, ClientCapabilities, Range, Position } from '../modes/languageModes'; +import { newSemanticTokenProvider } from '../modes/semanticTokens'; +import { getNodeFSRequestService } from '../node/nodeFs'; + +interface ExpectedToken { + startLine: number; + character: number; + length: number; + tokenClassifiction: string; +} + +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, getNodeFSRequestService()); + const semanticTokensProvider = newSemanticTokenProvider(languageModes); + + const legend = semanticTokensProvider.legend; + const actual = await semanticTokensProvider.getSemanticTokens(document, ranges); + + let actualRanges = []; + let lastLine = 0; + let lastCharacter = 0; + for (let i = 0; i < actual.length; i += 5) { + const lineDelta = actual[i], charDelta = actual[i + 1], len = actual[i + 2], typeIdx = actual[i + 3], modSet = actual[i + 4]; + const line = lastLine + lineDelta; + const character = lineDelta === 0 ? lastCharacter + charDelta : charDelta; + const tokenClassifiction = [legend.types[typeIdx], ...legend.modifiers.filter((_, i) => modSet & 1 << i)].join('.'); + actualRanges.push(t(line, character, len, tokenClassifiction)); + lastLine = line; + lastCharacter = character; + } + assert.deepEqual(actualRanges, expected, message); +} + +function t(startLine: number, character: number, length: number, tokenClassifiction: string): ExpectedToken { + return { startLine, character, length, tokenClassifiction }; +} + +suite('HTML Semantic Tokens', () => { + + test('Variables', async () => { + const input = [ + /*0*/'', + /*1*/'', + /*2*/'', + /*10*/'', + /*11*/'', + ]; + 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'), + t(7, 10, 2, 'variable') + ]); + }); + + test('Functions', async () => { + const input = [ + /*0*/'', + /*1*/'', + /*2*/'', + /*8*/'', + /*9*/'', + ]; + 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', async () => { + const input = [ + /*0*/'', + /*1*/'', + /*2*/'', + /*12*/'', + /*13*/'', + ]; + + + await assertTokens(input, [ + t(3, 8, 1, 'class.declaration'), + t(4, 11, 1, 'property.declaration.static'), + t(5, 4, 1, 'property.declaration'), + t(6, 10, 1, 'member.declaration.async'), t(6, 23, 1, 'class'), t(6, 25, 1, 'property.static'), t(6, 40, 1, 'member.async'), + t(7, 8, 1, 'property.declaration'), t(7, 26, 1, 'property'), + t(8, 11, 1, 'member.declaration.static'), t(8, 28, 1, 'class'), t(8, 32, 1, 'property'), + ]); + }); + + test('Interfaces', async () => { + const input = [ + /*0*/'', + /*1*/'', + /*2*/'', + /*7*/'', + /*8*/'', + ]; + 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', async () => { + const input = [ + /*0*/'', + /*1*/'', + /*2*/'', + /*8*/'', + /*9*/'', + ]; + 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'), + t(6, 2, 7, 'variable'), t(6, 10, 3, 'member'), t(6, 14, 1, 'variable.readonly'), t(6, 18, 1, 'class'), t(6, 20, 1, 'property.static.readonly'), t(6, 24, 1, 'class'), t(6, 26, 3, 'property.static'), t(6, 30, 6, 'property.readonly'), + ]); + }); + + + test('Type aliases and type parameters', async () => { + const input = [ + /*0*/'', + /*1*/'', + /*2*/'', + /*8*/'', + /*9*/'', + ]; + 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', async () => { + const input = [ + /*0*/'', + /*1*/'', + /*2*/'', + /*5*/'', + /*8*/'', + /*9*/'', + ]; + 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', async () => { + const input = [ + /*0*/'', + /*1*/'', + /*2*/'', + /*5*/'', + /*8*/'', + /*9*/'', + ]; + await assertTokens(input, [ + t(3, 2, 6, 'variable'), t(3, 9, 5, 'member') + ], [Range.create(Position.create(2, 0), Position.create(4, 0))]); + + 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 6c391064fae12..f8bc67f8b10c8 100644 --- a/extensions/html-language-features/server/src/utils/documentContext.ts +++ b/extensions/html-language-features/server/src/utils/documentContext.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DocumentContext } from 'vscode-html-languageservice'; +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 { @@ -23,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/positions.ts b/extensions/html-language-features/server/src/utils/positions.ts new file mode 100644 index 0000000000000..eefd7f61fd42f --- /dev/null +++ b/extensions/html-language-features/server/src/utils/positions.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 { Position, Range } from '../modes/languageModes'; + +export function beforeOrSame(p1: Position, p2: Position) { + return p1.line < p2.line || p1.line === p2.line && p1.character <= p2.character; +} +export function insideRangeButNotSame(r1: Range, r2: Range) { + return beforeOrSame(r1.start, r2.start) && beforeOrSame(r2.end, r1.end) && !equalRange(r1, r2); +} +export function equalRange(r1: Range, r2: Range) { + return r1.start.line === r2.start.line && r1.start.character === r2.start.character && r1.end.line === r2.end.line && r1.end.character === r2.end.character; +} diff --git a/extensions/html-language-features/server/src/utils/runner.ts b/extensions/html-language-features/server/src/utils/runner.ts index 98a7a96f9aa3c..6bf8a88d0c691 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 c1b6dc3a88880..5f7aa21e58aaf 100644 --- a/extensions/html-language-features/server/test/index.js +++ b/extensions/html-language-features/server/test/index.js @@ -11,7 +11,7 @@ const suite = 'Integration HTML Extension Tests'; const options = { ui: 'tdd', - useColors: true, + useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; @@ -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 f96dfc5df6203..e7fc4c92980b8 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -2,10 +2,10 @@ # 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/mocha@7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" + integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w== "@types/node@^12.11.7": version "12.11.7" @@ -34,6 +34,14 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +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" @@ -46,6 +54,11 @@ balanced-match@^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,6 +67,13 @@ 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" @@ -64,7 +84,7 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -chalk@^2.0.1: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -78,6 +98,21 @@ charenc@~0.0.1: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== + 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.2.0" + optionalDependencies: + fsevents "~2.1.1" + cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -187,6 +222,13 @@ esprima@^4.0.0: 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@3.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" @@ -206,6 +248,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.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -216,6 +263,13 @@ 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-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: + is-glob "^4.0.1" + glob@7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" @@ -228,10 +282,10 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -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: + 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" @@ -280,6 +334,13 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +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" @@ -300,11 +361,28 @@ is-date-object@^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-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: + is-extglob "^2.1.1" + +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-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" @@ -340,22 +418,17 @@ 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.16.4, lodash@^4.17.15: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== -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== +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== dependencies: - chalk "^2.0.1" + chalk "^2.4.2" md5@^2.1.0: version "2.2.1" @@ -378,17 +451,29 @@ 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: +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.5: + 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" + +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@^1.23.3: + 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" @@ -404,13 +489,14 @@ 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@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.2.tgz#8e40d198acf91a52ace122cd7599c9ab857b29e6" + integrity sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA== dependencies: ansi-colors "3.2.3" browser-stdout "1.3.1" + chokidar "3.3.0" debug "3.2.6" diff "3.5.0" escape-string-regexp "1.0.5" @@ -419,18 +505,18 @@ mocha@^6.1.4: growl "1.10.5" he "1.2.0" js-yaml "3.13.1" - log-symbols "2.2.0" + log-symbols "3.0.0" minimatch "3.0.4" - mkdirp "0.5.1" + mkdirp "0.5.5" ms "2.1.1" - node-environment-flags "1.0.5" + node-environment-flags "1.0.6" object.assign "4.1.0" strip-json-comments "2.0.1" supports-color "6.0.0" which "1.3.1" wide-align "1.1.3" - yargs "13.3.0" - yargs-parser "13.1.1" + yargs "13.3.2" + yargs-parser "13.1.2" yargs-unparser "1.6.0" ms@2.0.0: @@ -448,14 +534,19 @@ ms@^2.1.1: 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== +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== 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" @@ -520,6 +611,18 @@ path-is-absolute@^1.0.0: 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== + +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== + dependencies: + picomatch "^2.0.4" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -611,70 +714,72 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^4.0.3-next.19: - version "4.0.3-next.19" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.3-next.19.tgz#b7dc58fb8d1968877724e163b6ef20b26c0b5ff6" - integrity sha512-wWSo2MZvd8aEI9lf/Asy0MP0Ao6xAzhBeEWxGcF+Ms/zcV4lDRkyFCNzwDt0OgWRzsBNaEBXfjeWLq9rNXkREA== - 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" - -vscode-html-languageservice@^3.0.4-next.8: - version "3.0.4-next.8" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.0.4-next.8.tgz#b3df7b7e8f69c1bf76392c4fd4df4cf483e77e7d" - integrity sha512-gT34wzCwM1rCJzd0EAFnuVe0FaaSr3eXaJpYcMj6rt1UspIJYaL4WtDLCcw4eBL906N1b1Vu+sapiRmV5PjZpg== - 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-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== - dependencies: - vscode-jsonrpc "^5.0.0-next.2" - vscode-languageserver-types "^3.15.0-next.6" - -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-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== - dependencies: - vscode-languageserver-protocol "^3.15.0-next.10" - -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.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== +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-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-css-languageservice@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.3.3.tgz#fcb8c7442ae7bb8fbe6ff1d3a514be8248bfb52f" + integrity sha512-b2b+0oHvPmBHygDtOXX3xBvpQCa6eIQSvXnGDNSDmIC1894ZTJ2yX10vjplOO/PvV7mwhyvGPwHyY4X2HGxtKw== + dependencies: + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "3.16.0-next.2" + vscode-nls "^4.1.2" + vscode-uri "^2.1.2" + +vscode-html-languageservice@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-3.1.2.tgz#7598ecd99a314f3f6067c710faf9207ddf5875e7" + integrity sha512-ixdcyb2xBmwNTuMitT6vNbnvHCnATim1ZZC/CxiUtzKImQjuXAT5Px53x/7+zUqGU9RlfkEwHN//slWcCBMdMw== + dependencies: + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "3.16.0-next.2" + vscode-nls "^4.1.2" + vscode-uri "^2.1.2" + +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.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 "6.0.0-next.2" + vscode-languageserver-types "3.16.0-next.2" + +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.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@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@^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== + +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" @@ -719,7 +824,15 @@ 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: +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-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== @@ -736,7 +849,23 @@ yargs-unparser@1.6.0: lodash "^4.17.15" yargs "^13.3.0" -yargs@13.3.0, yargs@^13.3.0: +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" + 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 "^13.1.2" + +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== diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index f3528ff867a47..0d4b9507c21c3 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/build/update-grammar.js b/extensions/html/build/update-grammar.js new file mode 100644 index 0000000000000..3277cb16ccafc --- /dev/null +++ b/extensions/html/build/update-grammar.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. + *--------------------------------------------------------------------------------------------*/ +// @ts-check +'use strict'; + +var updateGrammar = require('../../../build/npm/update-grammar'); + +function patchGrammar(grammar) { + let patchCount = 0; + + let visit = function (rule, parent) { + if (rule.name === 'source.js' || rule.name === 'source.css') { + if (parent.parent && parent.parent.property === 'endCaptures') { + rule.name = rule.name + '-ignored-vscode'; + patchCount++; + } + } + for (let property in rule) { + let value = rule[property]; + if (typeof value === 'object') { + visit(value, { node: rule, property: property, parent: parent }); + } + } + }; + + let repository = grammar.repository; + for (let key in repository) { + visit(repository[key], { node: repository, property: key, parent: undefined }); + } + if (patchCount !== 6) { + console.warn(`Expected to patch 6 occurrences of source.js & source.css: Was ${patchCount}`); + } + + + return grammar; +} + +const tsGrammarRepo = 'textmate/html.tmbundle'; +const grammarPath = 'Syntaxes/HTML.plist'; +updateGrammar.update(tsGrammarRepo, grammarPath, './syntaxes/html.tmLanguage.json', grammar => patchGrammar(grammar)); + + diff --git a/extensions/html/package.json b/extensions/html/package.json index 1b28ff2e5eb1d..6b1eac2d70226 100644 --- a/extensions/html/package.json +++ b/extensions/html/package.json @@ -9,7 +9,7 @@ "vscode": "0.10.x" }, "scripts": { - "update-grammar": "node ../../build/npm/update-grammar.js textmate/html.tmbundle Syntaxes/HTML.plist ./syntaxes/html.tmLanguage.json Syntaxes/HTML%20%28Derivative%29.tmLanguage ./syntaxes/html-derivative.tmLanguage.json" + "update-grammar": "node ./build/update-grammar.js" }, "contributes": { "languages": [ @@ -20,6 +20,7 @@ ".htm", ".shtml", ".xhtml", + ".xht", ".mdoc", ".jsp", ".asp", diff --git a/extensions/html/syntaxes/html.tmLanguage.json b/extensions/html/syntaxes/html.tmLanguage.json index a071d90a9e319..1e1c85c899f05 100644 --- a/extensions/html/syntaxes/html.tmLanguage.json +++ b/extensions/html/syntaxes/html.tmLanguage.json @@ -108,7 +108,7 @@ "name": "punctuation.definition.string.end.html" }, "1": { - "name": "source.css" + "name": "source.css-ignored-vscode" } }, "name": "string.quoted.double.html", @@ -132,7 +132,7 @@ "name": "punctuation.definition.string.end.html" }, "1": { - "name": "source.css" + "name": "source.css-ignored-vscode" } }, "name": "string.quoted.single.html", @@ -207,7 +207,7 @@ "name": "punctuation.definition.string.end.html" }, "1": { - "name": "source.js" + "name": "source.js-ignored-vscode" } }, "name": "string.quoted.double.html", @@ -265,7 +265,7 @@ "name": "punctuation.definition.string.end.html" }, "1": { - "name": "source.js" + "name": "source.js-ignored-vscode" } }, "name": "string.quoted.single.html", @@ -1785,7 +1785,7 @@ "name": "punctuation.definition.tag.begin.html" }, "2": { - "name": "source.css" + "name": "source.css-ignored-vscode" }, "3": { "name": "entity.name.tag.html" @@ -1892,7 +1892,7 @@ "name": "punctuation.definition.tag.begin.html" }, "2": { - "name": "source.js" + "name": "source.js-ignored-vscode" } }, "patterns": [ diff --git a/extensions/html/test/colorize-fixtures/test-embedding.html b/extensions/html/test/colorize-fixtures/test-embedding.html new file mode 100644 index 0000000000000..87f92b6264ba4 --- /dev/null +++ b/extensions/html/test/colorize-fixtures/test-embedding.html @@ -0,0 +1,6 @@ + + + +
+
+
diff --git a/extensions/html/test/colorize-results/12750_html.json b/extensions/html/test/colorize-results/12750_html.json index c70c7afdb8bef..a4f0184b51c32 100644 --- a/extensions/html/test/colorize-results/12750_html.json +++ b/extensions/html/test/colorize-results/12750_html.json @@ -210,7 +210,7 @@ }, { "c": "<", - "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -397,7 +397,7 @@ }, { "c": "<", - "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/html/test/colorize-results/25920_html.json b/extensions/html/test/colorize-results/25920_html.json index 4f4b04e21f551..9aa58b607aac7 100644 --- a/extensions/html/test/colorize-results/25920_html.json +++ b/extensions/html/test/colorize-results/25920_html.json @@ -499,9 +499,9 @@ "t": "text.html.derivative meta.embedded.block.html source.js meta.var.expr.js constant.numeric.decimal.js", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -518,7 +518,7 @@ }, { "c": "<", - "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1011,4 +1011,4 @@ "hc_black": "punctuation.definition.tag: #808080" } } -] \ No newline at end of file +] diff --git a/extensions/html/test/colorize-results/test-embedding_html.json b/extensions/html/test/colorize-results/test-embedding_html.json new file mode 100644 index 0000000000000..cd3af42bf2c07 --- /dev/null +++ b/extensions/html/test/colorize-results/test-embedding_html.json @@ -0,0 +1,1003 @@ +[ + { + "c": "<", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "script", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "console", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js variable.other.object.js", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ".", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js punctuation.accessor.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "log", + "t": "text.html.derivative meta.embedded.block.html source.js meta.function-call.js entity.name.function.js", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "(", + "t": "text.html.derivative meta.embedded.block.html source.js meta.brace.round.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "'", + "t": "text.html.derivative meta.embedded.block.html source.js string.quoted.single.js punctuation.definition.string.begin.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "x", + "t": "text.html.derivative meta.embedded.block.html source.js string.quoted.single.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "text.html.derivative meta.embedded.block.html source.js string.quoted.single.js punctuation.definition.string.end.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "text.html.derivative meta.embedded.block.html source.js meta.brace.round.js", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js-ignored-vscode", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "/", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "script", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "<", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.start.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "style", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.start.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.start.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "test", + "t": "text.html.derivative meta.embedded.block.html source.css meta.selector.css", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.embedded.block.html source.css", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "{", + "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css punctuation.section.property-list.begin.bracket.curly.css", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "display", + "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css meta.property-name.css support.type.property-name.css", + "r": { + "dark_plus": "support.type.property-name: #9CDCFE", + "light_plus": "support.type.property-name: #FF0000", + "dark_vs": "support.type.property-name: #9CDCFE", + "light_vs": "support.type.property-name: #FF0000", + "hc_black": "support.type.property-name: #D4D4D4" + } + }, + { + "c": ":", + "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css punctuation.separator.key-value.css", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "none", + "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css meta.property-value.css support.constant.property-value.css", + "r": { + "dark_plus": "support.constant.property-value: #CE9178", + "light_plus": "support.constant.property-value: #0451A5", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "support.constant.property-value: #0451A5", + "hc_black": "support.constant.property-value: #CE9178" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "}", + "t": "text.html.derivative meta.embedded.block.html source.css meta.property-list.css punctuation.section.property-list.end.bracket.curly.css", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "<", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html source.css-ignored-vscode", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "/", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "style", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.end.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": ">", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "<", + "t": "text.html.derivative meta.tag.inline.a.start.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "a", + "t": "text.html.derivative meta.tag.inline.a.start.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.tag.inline.a.start.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "onblur", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.blur.html entity.other.attribute-name.html", + "r": { + "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" + } + }, + { + "c": "=", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.blur.html punctuation.separator.key-value.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.blur.html meta.embedded.line.js string.quoted.double.html punctuation.definition.string.begin.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "doBlur", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.blur.html meta.embedded.line.js string.quoted.double.html source.js meta.function-call.js entity.name.function.js", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.blur.html meta.embedded.line.js string.quoted.double.html source.js meta.brace.round.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.blur.html meta.embedded.line.js string.quoted.double.html punctuation.definition.string.end.html source.js-ignored-vscode", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.tag.inline.a.start.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "onclick", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.click.html entity.other.attribute-name.html", + "r": { + "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" + } + }, + { + "c": "=", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.click.html punctuation.separator.key-value.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.click.html meta.embedded.line.js string.quoted.single.html punctuation.definition.string.begin.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "doClick", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.click.html meta.embedded.line.js string.quoted.single.html source.js meta.function-call.js entity.name.function.js", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.click.html meta.embedded.line.js string.quoted.single.html source.js meta.brace.round.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.click.html meta.embedded.line.js string.quoted.single.html punctuation.definition.string.end.html source.js-ignored-vscode", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.tag.inline.a.start.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "ondrag", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.drag.html entity.other.attribute-name.html", + "r": { + "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" + } + }, + { + "c": "=", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.drag.html punctuation.separator.key-value.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "doDrag", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.drag.html meta.embedded.line.js string.unquoted.html meta.function-call.js entity.name.function.js", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.html: #0000FF", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": "()", + "t": "text.html.derivative meta.tag.inline.a.start.html meta.attribute.event-handler.drag.html meta.embedded.line.js string.unquoted.html meta.brace.round.js", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.html.derivative meta.tag.inline.a.start.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "", + "t": "text.html.derivative meta.tag.inline.a.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "<", + "t": "text.html.derivative meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "div", + "t": "text.html.derivative meta.tag.structure.div.start.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.tag.structure.div.start.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "style", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html entity.other.attribute-name.html", + "r": { + "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" + } + }, + { + "c": "=", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html punctuation.separator.key-value.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html meta.embedded.line.css string.quoted.double.html punctuation.definition.string.begin.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "x { }", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html meta.embedded.line.css string.quoted.double.html source.css", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\"", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html meta.embedded.line.css string.quoted.double.html punctuation.definition.string.end.html source.css-ignored-vscode", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.double.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.double.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.html.derivative meta.tag.structure.div.start.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "", + "t": "text.html.derivative meta.tag.structure.div.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "<", + "t": "text.html.derivative meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "div", + "t": "text.html.derivative meta.tag.structure.div.start.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.tag.structure.div.start.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "style", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html entity.other.attribute-name.html", + "r": { + "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" + } + }, + { + "c": "=", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html punctuation.separator.key-value.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "'", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html meta.embedded.line.css string.quoted.single.html punctuation.definition.string.begin.html", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "y { }", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html meta.embedded.line.css string.quoted.single.html source.css", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": "'", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html meta.embedded.line.css string.quoted.single.html punctuation.definition.string.end.html source.css-ignored-vscode", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.html.derivative meta.tag.structure.div.start.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "", + "t": "text.html.derivative meta.tag.structure.div.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "<", + "t": "text.html.derivative meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "div", + "t": "text.html.derivative meta.tag.structure.div.start.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.derivative meta.tag.structure.div.start.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "style", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html entity.other.attribute-name.html", + "r": { + "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" + } + }, + { + "c": "=", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html punctuation.separator.key-value.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "z{}", + "t": "text.html.derivative meta.tag.structure.div.start.html meta.attribute.style.html meta.embedded.line.css string.unquoted.html source.css", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.html: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.html: #0000FF", + "hc_black": "string: #CE9178" + } + }, + { + "c": ">", + "t": "text.html.derivative meta.tag.structure.div.start.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "", + "t": "text.html.derivative meta.tag.structure.div.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + } +] \ No newline at end of file diff --git a/extensions/html/test/colorize-results/test_html.json b/extensions/html/test/colorize-results/test_html.json index 970d4214a7419..dd8a4aa2043c1 100644 --- a/extensions/html/test/colorize-results/test_html.json +++ b/extensions/html/test/colorize-results/test_html.json @@ -782,7 +782,7 @@ }, { "c": "<", - "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html source.css", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html source.css-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1200,7 +1200,7 @@ }, { "c": "<", - "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1354,7 +1354,7 @@ }, { "c": "<", - "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2212,7 +2212,7 @@ }, { "c": "<", - "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.derivative meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3189,4 +3189,4 @@ "hc_black": "punctuation.definition.tag: #808080" } } -] +] \ No newline at end of file diff --git a/extensions/image-preview/.vscodeignore b/extensions/image-preview/.vscodeignore index 30d948fbc6610..bcb886a094d89 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/README.md b/extensions/image-preview/README.md index ccb5ac3954af6..d3f0bd6cb6c64 100644 --- a/extensions/image-preview/README.md +++ b/extensions/image-preview/README.md @@ -13,5 +13,4 @@ Supported image formats: - `*.bmp` - `*.gif` - `*.ico` -- `*.tga` - `*.webp` 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 0000000000000..9a1bb4d3c8e8c --- /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.css b/extensions/image-preview/media/main.css index ff3e6d0f0d2e3..49a01b8d9694b 100644 --- a/extensions/image-preview/media/main.css +++ b/extensions/image-preview/media/main.css @@ -94,16 +94,16 @@ body img { } .loading-indicator, -.image-load-error-message { +.image-load-error { display: none; } .loading .loading-indicator, -.error .image-load-error-message { +.error .image-load-error { display: block; } -.image-load-error-message { +.image-load-error { margin: 1em; } diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js index a657ca04e4025..d344b99fbaf29 100644 --- a/extensions/image-preview/media/main.js +++ b/extensions/image-preview/media/main.js @@ -241,7 +241,11 @@ }); container.addEventListener('wheel', (/** @type {WheelEvent} */ e) => { - e.preventDefault(); + // Prevent pinch to zoom + if (e.ctrlKey) { + e.preventDefault(); + } + if (!image || !hasLoadedImage) { return; } @@ -260,8 +264,6 @@ }, { passive: false }); window.addEventListener('scroll', e => { - e.preventDefault(); - if (!image || !hasLoadedImage || !image.parentElement || scale === 'fit') { return; } @@ -270,13 +272,16 @@ if (entry) { vscode.setState({ scale: entry.scale, offsetX: window.scrollX, offsetY: window.scrollY }); } - }, { passive: false }); + }, { passive: true }); container.classList.add('image'); image.classList.add('scale-to-fit'); image.addEventListener('load', () => { + if (hasLoadedImage) { + return; + } hasLoadedImage = true; vscode.postMessage({ @@ -295,13 +300,23 @@ } }); - image.addEventListener('error', () => { + image.addEventListener('error', e => { + if (hasLoadedImage) { + return; + } + hasLoadedImage = true; document.body.classList.add('error'); document.body.classList.remove('loading'); }); - image.src = decodeURI(settings.src); + image.src = settings.src; + + document.querySelector('.open-file-link').addEventListener('click', () => { + vscode.postMessage({ + type: 'reopen-as-text', + }); + }); window.addEventListener('message', e => { switch (e.data.type) { diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index c6149de85decc..064ada948586d 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", + "extensionKind": [ + "ui", + "workspace", + "web" + ], "version": "1.0.0", "publisher": "vscode", "icon": "icon.png", @@ -13,24 +17,24 @@ "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": [ { - "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,tga,webp}", - "mime": "image/*" + "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,webp}" } ] } @@ -66,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 304b1df9a3d70..d1860bc2fb5b7 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/binarySizeStatusBarEntry.ts b/extensions/image-preview/src/binarySizeStatusBarEntry.ts new file mode 100644 index 0000000000000..0c983d37cd216 --- /dev/null +++ b/extensions/image-preview/src/binarySizeStatusBarEntry.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { PreviewStatusBarEntry } from './ownedStatusBarEntry'; + +const localize = nls.loadMessageBundle(); + +class BinarySize { + static readonly KB = 1024; + static readonly MB = BinarySize.KB * BinarySize.KB; + static readonly GB = BinarySize.MB * BinarySize.KB; + static readonly TB = BinarySize.GB * BinarySize.KB; + + static formatSize(size: number): string { + if (size < BinarySize.KB) { + return localize('sizeB', "{0}B", size); + } + + if (size < BinarySize.MB) { + return localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2)); + } + + if (size < BinarySize.GB) { + return localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2)); + } + + if (size < BinarySize.TB) { + return localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2)); + } + + return localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2)); + } +} + +export class BinarySizeStatusBarEntry extends PreviewStatusBarEntry { + + constructor() { + super({ + id: 'imagePreview.binarySize', + name: localize('sizeStatusBar.name', "Image Binary Size"), + alignment: vscode.StatusBarAlignment.Right, + priority: 100, + }); + } + + public show(owner: string, size: number | undefined) { + if (typeof size === 'number') { + super.showItem(owner, BinarySize.formatSize(size)); + } else { + this.hide(owner); + } + } +} diff --git a/extensions/image-preview/src/extension.ts b/extensions/image-preview/src/extension.ts index cd68edf2e5c87..10722360dd594 100644 --- a/extensions/image-preview/src/extension.ts +++ b/extensions/image-preview/src/extension.ts @@ -4,29 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; import { PreviewManager } from './preview'; import { SizeStatusBarEntry } from './sizeStatusBarEntry'; 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); + const binarySizeStatusBarEntry = new BinarySizeStatusBarEntry(); + context.subscriptions.push(binarySizeStatusBarEntry); + const zoomStatusBarEntry = new ZoomStatusBarEntry(); context.subscriptions.push(zoomStatusBarEntry); - const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, zoomStatusBarEntry); + const previewManager = new PreviewManager(context.extensionUri, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry); - context.subscriptions.push(vscode.window.registerWebviewEditorProvider( - PreviewManager.viewType, - { - async resolveWebviewEditor({ resource }, editor: vscode.WebviewPanel): Promise { - previewManager.resolve(resource, editor); - return {}; - } - })); + context.subscriptions.push(vscode.window.registerCustomEditorProvider(PreviewManager.viewType, previewManager, { + supportsMultipleEditorsPerDocument: true, + })); context.subscriptions.push(vscode.commands.registerCommand('imagePreview.zoomIn', () => { previewManager.activePreview?.zoomIn(); @@ -36,4 +33,3 @@ export function activate(context: vscode.ExtensionContext) { previewManager.activePreview?.zoomOut(); })); } - diff --git a/extensions/image-preview/src/ownedStatusBarEntry.ts b/extensions/image-preview/src/ownedStatusBarEntry.ts new file mode 100644 index 0000000000000..7be0ec30f7178 --- /dev/null +++ b/extensions/image-preview/src/ownedStatusBarEntry.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Disposable } from './dispose'; + +export abstract class PreviewStatusBarEntry extends Disposable { + private _showOwner: string | undefined; + + protected readonly entry: vscode.StatusBarItem; + + constructor(options: vscode.window.StatusBarItemOptions) { + super(); + this.entry = this._register(vscode.window.createStatusBarItem(options)); + } + + protected showItem(owner: string, text: string) { + this._showOwner = owner; + this.entry.text = text; + this.entry.show(); + } + + public hide(owner: string) { + if (owner === this._showOwner) { + this.entry.hide(); + this._showOwner = undefined; + } + } +} diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 1b9c8d3f3222d..f4b589e0fb864 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -8,11 +8,12 @@ import * as nls from 'vscode-nls'; import { Disposable } from './dispose'; import { SizeStatusBarEntry } from './sizeStatusBarEntry'; import { Scale, ZoomStatusBarEntry } from './zoomStatusBarEntry'; +import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry'; const localize = nls.loadMessageBundle(); -export class PreviewManager { +export class PreviewManager implements vscode.CustomReadonlyEditorProvider { public static readonly viewType = 'imagePreview.previewEditor'; @@ -22,14 +23,19 @@ export class PreviewManager { constructor( private readonly extensionRoot: vscode.Uri, private readonly sizeStatusBarEntry: SizeStatusBarEntry, + private readonly binarySizeStatusBarEntry: BinarySizeStatusBarEntry, private readonly zoomStatusBarEntry: ZoomStatusBarEntry, ) { } - public resolve( - resource: vscode.Uri, + public async openCustomDocument(uri: vscode.Uri) { + return { uri, dispose: () => { } }; + } + + public async resolveCustomEditor( + document: vscode.CustomDocument, webviewEditor: vscode.WebviewPanel, - ) { - const preview = new Preview(this.extensionRoot, resource, webviewEditor, this.sizeStatusBarEntry, this.zoomStatusBarEntry); + ): Promise { + const preview = new Preview(this.extensionRoot, document.uri, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry); this._previews.add(preview); this.setActivePreview(preview); @@ -68,13 +74,17 @@ class Preview extends Disposable { private _previewState = PreviewState.Visible; private _imageSize: string | undefined; + private _imageBinarySize: number | undefined; private _imageZoom: Scale | undefined; + private readonly emptyPngDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg=='; + constructor( private readonly extensionRoot: vscode.Uri, private readonly resource: vscode.Uri, private readonly webviewEditor: vscode.WebviewPanel, private readonly sizeStatusBarEntry: SizeStatusBarEntry, + private readonly binarySizeStatusBarEntry: BinarySizeStatusBarEntry, private readonly zoomStatusBarEntry: ZoomStatusBarEntry, ) { super(); @@ -104,6 +114,12 @@ class Preview extends Disposable { this.update(); break; } + + case 'reopen-as-text': + { + vscode.commands.executeCommand('vscode.openWith', resource, 'default', webviewEditor.viewColumn); + break; + } } })); @@ -121,6 +137,7 @@ class Preview extends Disposable { this._register(webviewEditor.onDidDispose(() => { if (this._previewState === PreviewState.Active) { this.sizeStatusBarEntry.hide(this.id); + this.binarySizeStatusBarEntry.hide(this.id); this.zoomStatusBarEntry.hide(this.id); } this._previewState = PreviewState.Disposed; @@ -138,6 +155,11 @@ class Preview extends Disposable { } })); + vscode.workspace.fs.stat(resource).then(({ size }) => { + this._imageBinarySize = size; + this.update(); + }); + this.render(); this.update(); this.webviewEditor.webview.postMessage({ type: 'setActive', value: this.webviewEditor.active }); @@ -155,9 +177,9 @@ class Preview extends Disposable { } } - private render() { + private async render() { if (this._previewState !== PreviewState.Disposed) { - this.webviewEditor.webview.html = this.getWebiewContents(); + this.webviewEditor.webview.html = await this.getWebviewContents(); } } @@ -169,21 +191,23 @@ class Preview extends Disposable { if (this.webviewEditor.active) { this._previewState = PreviewState.Active; this.sizeStatusBarEntry.show(this.id, this._imageSize || ''); + this.binarySizeStatusBarEntry.show(this.id, this._imageBinarySize); this.zoomStatusBarEntry.show(this.id, this._imageZoom || 'fit'); } else { if (this._previewState === PreviewState.Active) { this.sizeStatusBarEntry.hide(this.id); + this.binarySizeStatusBarEntry.hide(this.id); this.zoomStatusBarEntry.hide(this.id); } this._previewState = PreviewState.Visible; } } - private getWebiewContents(): string { + private async getWebviewContents(): Promise { const version = Date.now().toString(); const settings = { isMac: process.platform === 'darwin', - src: this.getResourcePath(this.webviewEditor, this.resource, version), + src: await this.getResourcePath(this.webviewEditor, this.resource, version), }; const nonce = Date.now().toString(); @@ -192,8 +216,11 @@ class Preview extends Disposable { - - + + + + Image Preview @@ -203,25 +230,28 @@ class Preview extends Disposable {
-
${localize('preview.imageLoadError', "An error occurred while loading the image")}
+
+

${localize('preview.imageLoadError', "An error occurred while loading the image.")}

+ ${localize('preview.imageLoadErrorLink', "Open file using VS Code's standard text/binary editor?")} +
`; } - private getResourcePath(webviewEditor: vscode.WebviewPanel, resource: vscode.Uri, version: string) { - switch (resource.scheme) { - case 'data': - return encodeURI(resource.toString(true)); - - case 'git': - // Show blank image - return encodeURI('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg=='); - + private async getResourcePath(webviewEditor: vscode.WebviewPanel, resource: vscode.Uri, version: string): Promise { + if (resource.scheme === 'git') { + const stat = await vscode.workspace.fs.stat(resource); + if (stat.size === 0) { + return this.emptyPngDataUri; + } + } - default: - return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true) + `?version=${version}`); + // Avoid adding cache busting if there is already a query string + if (resource.query) { + return webviewEditor.webview.asWebviewUri(resource).toString(); } + return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(); } private extensionResource(path: string) { diff --git a/extensions/image-preview/src/sizeStatusBarEntry.ts b/extensions/image-preview/src/sizeStatusBarEntry.ts index 6b2bff746cfdc..e74eea0fe6032 100644 --- a/extensions/image-preview/src/sizeStatusBarEntry.ts +++ b/extensions/image-preview/src/sizeStatusBarEntry.ts @@ -4,36 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { Disposable } from './dispose'; import * as nls from 'vscode-nls'; +import { PreviewStatusBarEntry } from './ownedStatusBarEntry'; const localize = nls.loadMessageBundle(); -export class SizeStatusBarEntry extends Disposable { - private readonly _entry: vscode.StatusBarItem; - - private _showingOwner: string | undefined; +export class SizeStatusBarEntry extends PreviewStatusBarEntry { constructor() { - super(); - this._entry = this._register(vscode.window.createStatusBarItem({ + super({ id: 'imagePreview.size', name: localize('sizeStatusBar.name', "Image Size"), alignment: vscode.StatusBarAlignment.Right, priority: 101 /* to the left of editor status (100) */, - })); + }); } public show(owner: string, text: string) { - this._showingOwner = owner; - this._entry.text = text; - this._entry.show(); - } - - public hide(owner: string) { - if (owner === this._showingOwner) { - this._entry.hide(); - this._showingOwner = undefined; - } + this.showItem(owner, text); } } diff --git a/extensions/image-preview/src/zoomStatusBarEntry.ts b/extensions/image-preview/src/zoomStatusBarEntry.ts index dc102a48d6498..18adc19d6d2bc 100644 --- a/extensions/image-preview/src/zoomStatusBarEntry.ts +++ b/extensions/image-preview/src/zoomStatusBarEntry.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import { Disposable } from './dispose'; +import { PreviewStatusBarEntry as OwnedStatusBarEntry } from './ownedStatusBarEntry'; const localize = nls.loadMessageBundle(); @@ -13,22 +13,18 @@ const selectZoomLevelCommandId = '_imagePreview.selectZoomLevel'; export type Scale = number | 'fit'; -export class ZoomStatusBarEntry extends Disposable { - private readonly _entry: vscode.StatusBarItem; +export class ZoomStatusBarEntry extends OwnedStatusBarEntry { private readonly _onDidChangeScale = this._register(new vscode.EventEmitter<{ scale: Scale }>()); public readonly onDidChangeScale = this._onDidChangeScale.event; - private _showOwner: string | undefined; - constructor() { - super(); - this._entry = this._register(vscode.window.createStatusBarItem({ + super({ id: 'imagePreview.zoom', name: localize('zoomStatusBar.name', "Image Zoom"), alignment: vscode.StatusBarAlignment.Right, priority: 102 /* to the left of editor size entry (101) */, - })); + }); this._register(vscode.commands.registerCommand(selectZoomLevelCommandId, async () => { type MyPickItem = vscode.QuickPickItem & { scale: Scale }; @@ -47,20 +43,11 @@ export class ZoomStatusBarEntry extends Disposable { } })); - this._entry.command = selectZoomLevelCommandId; + this.entry.command = selectZoomLevelCommandId; } public show(owner: string, scale: Scale) { - this._showOwner = owner; - this._entry.text = this.zoomLabel(scale); - this._entry.show(); - } - - public hide(owner: string) { - if (owner === this._showOwner) { - this._entry.hide(); - this._showOwner = undefined; - } + this.showItem(owner, this.zoomLabel(scale)); } private zoomLabel(scale: Scale): string { diff --git a/extensions/ini/ini.language-configuration.json b/extensions/ini/ini.language-configuration.json index 93cfa1ce3b9d2..c688aee426d69 100644 --- a/extensions/ini/ini.language-configuration.json +++ b/extensions/ini/ini.language-configuration.json @@ -12,8 +12,8 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -22,4 +22,4 @@ ["\"", "\""], ["'", "'"] ] -} \ No newline at end of file +} diff --git a/extensions/ini/package.json b/extensions/ini/package.json index cc5ca5da6063e..24d86072749bf 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/ini/properties.language-configuration.json b/extensions/ini/properties.language-configuration.json index 85b401f07676c..7dd2e4ebe2e81 100644 --- a/extensions/ini/properties.language-configuration.json +++ b/extensions/ini/properties.language-configuration.json @@ -12,7 +12,7 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + { "open": "\"", "close": "\"", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -21,4 +21,4 @@ ["\"", "\""], ["'", "'"] ] -} \ No newline at end of file +} diff --git a/extensions/jake/src/main.ts b/extensions/jake/src/main.ts index b8c6d5e734446..93fa1aa285b8d 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 762917e98024f..68e15fac2df82 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": "123beb50115b0bfb0db8f2325df6d05fb8a016a8" + "commitHash": "f631536a4137d65b622f01cebeac72345f9e6f70" } }, "license": "MIT", - "version": "0.31.3" + "version": "0.31.5" } ], "version": 1 diff --git a/extensions/java/language-configuration.json b/extensions/java/language-configuration.json index a2909abb800e9..e19d2d749f875 100644 --- a/extensions/java/language-configuration.json +++ b/extensions/java/language-configuration.json @@ -12,8 +12,8 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"], + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] }, { "open": "/**", "close": " */", "notIn": ["string"] } ], "surroundingPairs": [ diff --git a/extensions/java/package.json b/extensions/java/package.json index 9d3310681b914..96f8c8c4adf09 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 f806edf7b6d8a..11b6cf50a9394 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/123beb50115b0bfb0db8f2325df6d05fb8a016a8", + "version": "https://github.com/atom/language-java/commit/f631536a4137d65b622f01cebeac72345f9e6f70", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -209,7 +209,7 @@ "name": "keyword.control.new.java" } }, - "end": "(?=;|\\)|,|:|}|\\+|\\-|\\*|\\/|%|!|&|\\||=)", + "end": "(?=;|\\)|\\]|\\.|,|\\?|:|}|\\+|\\-|\\*|\\/(?!\\/|\\*)|%|!|&|\\||\\^|=)", "patterns": [ { "include": "#comments" @@ -221,22 +221,60 @@ "include": "#all-types" }, { - "begin": "(?)?(\\()", + "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": [ { @@ -1472,7 +1666,7 @@ ] }, { - "begin": "\\b(catch)\\b\\s*(?=\\(\\s*[^\\s]+\\s*[^)]+\\))", + "begin": "\\b(catch)\\b", "beginCaptures": { "1": { "name": "keyword.control.catch.java" @@ -1486,6 +1680,9 @@ }, "name": "meta.catch.java", "patterns": [ + { + "include": "#comments" + }, { "begin": "\\(", "beginCaptures": { @@ -1508,19 +1705,31 @@ "include": "#storage-modifiers" }, { - "match": "\\|", - "name": "punctuation.catch.separator.java" - }, - { - "match": "([a-zA-Z$_][\\.a-zA-Z0-9$_]*)\\s*(\\w+)?", - "captures": { - "1": { + "begin": "[a-zA-Z$_][\\.a-zA-Z0-9$_]*", + "beginCaptures": { + "0": { "name": "storage.type.java" + } + }, + "end": "(\\|)|(?=\\))", + "endCaptures": { + "1": { + "name": "punctuation.catch.separator.java" + } + }, + "patterns": [ + { + "include": "#comments" }, - "2": { - "name": "variable.parameter.java" + { + "match": "\\w+", + "captures": { + "0": { + "name": "variable.parameter.java" + } + } } - } + ] } ] }, @@ -1576,7 +1785,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/java/test/colorize-results/basic_java.json b/extensions/java/test/colorize-results/basic_java.json index 0032d405471e8..ad37c62402e4d 100644 --- a/extensions/java/test/colorize-results/basic_java.json +++ b/extensions/java/test/colorize-results/basic_java.json @@ -763,9 +763,9 @@ "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -862,9 +862,9 @@ "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -961,9 +961,9 @@ "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1434,9 +1434,9 @@ "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1489,9 +1489,9 @@ "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1786,9 +1786,9 @@ "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2083,9 +2083,9 @@ "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.hex.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2309,4 +2309,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/javascript/package.json b/extensions/javascript/package.json index 8d315c4583560..d63bebded1625 100644 --- a/extensions/javascript/package.json +++ b/extensions/javascript/package.json @@ -32,6 +32,7 @@ ".js", ".es6", ".mjs", + ".cjs", ".pac" ], "filenames": [ @@ -89,14 +90,42 @@ "path": "./syntaxes/Regular Expressions (JavaScript).tmLanguage" } ], + "semanticTokenScopes": [ + { + "language": "javascript", + "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", + "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.json" + "path": "./snippets/javascript.code-snippets" }, { "language": "javascriptreact", - "path": "./snippets/javascript.json" + "path": "./snippets/javascript.code-snippets" } ] } diff --git a/extensions/javascript/snippets/javascript.code-snippets b/extensions/javascript/snippets/javascript.code-snippets new file mode 100644 index 0000000000000..b005c80c8445a --- /dev/null +++ b/extensions/javascript/snippets/javascript.code-snippets @@ -0,0 +1,194 @@ +{ + "define module": { + "prefix": "define", + "body": [ + "define([", + "\t'require',", + "\t'${1:dependency}'", + "], function(require, ${2:factory}) {", + "\t'use strict';", + "\t$0", + "});" + ], + "description": "define module" + }, + "For Loop": { + "prefix": "for", + "body": [ + "for (let ${1:index} = 0; ${1:index} < ${2:array}.length; ${1:index}++) {", + "\tconst ${3:element} = ${2:array}[${1:index}];", + "\t$0", + "}" + ], + "description": "For Loop" + }, + "For-Each Loop": { + "prefix": "foreach", + "body": [ + "${1:array}.forEach(${2:element} => {", + "\t$0", + "});" + ], + "description": "For-Each Loop" + }, + "For-In Loop": { + "prefix": "forin", + "body": [ + "for (const ${1:key} in ${2:object}) {", + "\tif (${2:object}.hasOwnProperty(${1:key})) {", + "\t\tconst ${3:element} = ${2:object}[${1:key}];", + "\t\t$0", + "\t}", + "}" + ], + "description": "For-In Loop" + }, + "For-Of Loop": { + "prefix": "forof", + "body": [ + "for (const ${1:iterator} of ${2:object}) {", + "\t$0", + "}" + ], + "description": "For-Of Loop" + }, + "Function Statement": { + "prefix": "function", + "body": [ + "function ${1:name}(${2:params}) {", + "\t$0", + "}" + ], + "description": "Function Statement" + }, + "If Statement": { + "prefix": "if", + "body": [ + "if (${1:condition}) {", + "\t$0", + "}" + ], + "description": "If Statement" + }, + "If-Else Statement": { + "prefix": "ifelse", + "body": [ + "if (${1:condition}) {", + "\t$0", + "} else {", + "\t", + "}" + ], + "description": "If-Else Statement" + }, + "New Statement": { + "prefix": "new", + "body": [ + "const ${1:name} = new ${2:type}(${3:arguments});$0" + ], + "description": "New Statement" + }, + "Switch Statement": { + "prefix": "switch", + "body": [ + "switch (${1:key}) {", + "\tcase ${2:value}:", + "\t\t$0", + "\t\tbreak;", + "", + "\tdefault:", + "\t\tbreak;", + "}" + ], + "description": "Switch Statement" + }, + "While Statement": { + "prefix": "while", + "body": [ + "while (${1:condition}) {", + "\t$0", + "}" + ], + "description": "While Statement" + }, + "Do-While Statement": { + "prefix": "dowhile", + "body": [ + "do {", + "\t$0", + "} while (${1:condition});" + ], + "description": "Do-While Statement" + }, + "Try-Catch Statement": { + "prefix": "trycatch", + "body": [ + "try {", + "\t$0", + "} catch (${1:error}) {", + "\t", + "}" + ], + "description": "Try-Catch Statement" + }, + "Set Timeout Function": { + "prefix": "settimeout", + "body": [ + "setTimeout(() => {", + "\t$0", + "}, ${1:timeout});" + ], + "description": "Set Timeout Function" + }, + "Set Interval Function": { + "prefix": "setinterval", + "body": [ + "setInterval(() => {", + "\t$0", + "}, ${1:interval});" + ], + "description": "Set Interval Function" + }, + "Import external module.": { + "prefix": "import statement", + "body": [ + "import { $0 } from \"${1:module}\";" + ], + "description": "Import external module." + }, + "Region Start": { + "prefix": "#region", + "body": [ + "//#region $0" + ], + "description": "Folding Region Start" + }, + "Region End": { + "prefix": "#endregion", + "body": [ + "//#endregion" + ], + "description": "Folding Region End" + }, + "Log to the console": { + "prefix": "log", + "body": [ + "console.log($1);" + ], + "description": "Log to the console" + }, + "Log warning to console": { + "prefix": "warn", + "body": [ + "console.warn($1);" + ], + "description": "Log warning to the console" + }, + "Log error to console": { + "prefix": "error", + "body": [ + "console.error($1);" + ], + "description": "Log error to the console" + } +} diff --git a/extensions/javascript/snippets/javascript.json b/extensions/javascript/snippets/javascript.json deleted file mode 100644 index 5da4ebe0c1883..0000000000000 --- a/extensions/javascript/snippets/javascript.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "define module": { - "prefix": "define", - "body": [ - "define([", - "\t'require',", - "\t'${1:dependency}'", - "], function(require, ${2:factory}) {", - "\t'use strict';", - "\t$0", - "});" - ], - "description": "define module" - }, - "For Loop": { - "prefix": "for", - "body": [ - "for (let ${1:index} = 0; ${1:index} < ${2:array}.length; ${1:index}++) {", - "\tconst ${3:element} = ${2:array}[${1:index}];", - "\t$0", - "}" - ], - "description": "For Loop" - }, - "For-Each Loop": { - "prefix": "foreach", - "body": [ - "${1:array}.forEach(${2:element} => {", - "\t$0", - "});" - ], - "description": "For-Each Loop" - }, - "For-In Loop": { - "prefix": "forin", - "body": [ - "for (const ${1:key} in ${2:object}) {", - "\tif (${2:object}.hasOwnProperty(${1:key})) {", - "\t\tconst ${3:element} = ${2:object}[${1:key}];", - "\t\t$0", - "\t}", - "}" - ], - "description": "For-In Loop" - }, - "For-Of Loop": { - "prefix": "forof", - "body": [ - "for (const ${1:iterator} of ${2:object}) {", - "\t$0", - "}" - ], - "description": "For-Of Loop" - }, - "Function Statement": { - "prefix": "function", - "body": [ - "function ${1:name}(${2:params}) {", - "\t$0", - "}" - ], - "description": "Function Statement" - }, - "If Statement": { - "prefix": "if", - "body": [ - "if (${1:condition}) {", - "\t$0", - "}" - ], - "description": "If Statement" - }, - "If-Else Statement": { - "prefix": "ifelse", - "body": [ - "if (${1:condition}) {", - "\t$0", - "} else {", - "\t", - "}" - ], - "description": "If-Else Statement" - }, - "New Statement": { - "prefix": "new", - "body": [ - "const ${1:name} = new ${2:type}(${3:arguments});$0" - ], - "description": "New Statement" - }, - "Switch Statement": { - "prefix": "switch", - "body": [ - "switch (${1:key}) {", - "\tcase ${2:value}:", - "\t\t$0", - "\t\tbreak;", - "", - "\tdefault:", - "\t\tbreak;", - "}" - ], - "description": "Switch Statement" - }, - "While Statement": { - "prefix": "while", - "body": [ - "while (${1:condition}) {", - "\t$0", - "}" - ], - "description": "While Statement" - }, - "Do-While Statement": { - "prefix": "dowhile", - "body": [ - "do {", - "\t$0", - "} while (${1:condition});" - ], - "description": "Do-While Statement" - }, - "Try-Catch Statement": { - "prefix": "trycatch", - "body": [ - "try {", - "\t$0", - "} catch (${1:error}) {", - "\t", - "}" - ], - "description": "Try-Catch Statement" - }, - "Set Timeout Function": { - "prefix": "settimeout", - "body": [ - "setTimeout(() => {", - "\t$0", - "}, ${1:timeout});" - ], - "description": "Set Timeout Function" - }, - "Set Interval Function": { - "prefix": "setinterval", - "body": [ - "setInterval(() => {", - "\t$0", - "}, ${1:interval});" - ], - "description": "Set Interval Function" - }, - "Import external module.": { - "prefix": "import statement", - "body": [ - "import { $0 } from \"${1:module}\";" - ], - "description": "Import external module." - }, - "Region Start": { - "prefix": "#region", - "body": [ - "//#region $0" - ], - "description": "Folding Region Start" - }, - "Region End": { - "prefix": "#endregion", - "body": [ - "//#endregion" - ], - "description": "Folding Region End" - }, - "Log to the console": { - "prefix": "log", - "body": [ - "console.log($1);", - "$0" - ], - "description": "Log to the console" - }, - "Log warning to console": { - "prefix": "warn", - "body": [ - "console.warn($1);", - "$0" - ], - "description": "Log warning to the console" - }, - "Log error to console": { - "prefix": "error", - "body": [ - "console.error($1);", - "$0" - ], - "description": "Log error to the console" - } -} diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 47894898ab3dd..a3daae7694330 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.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/TypeScript-TmLanguage/commit/477c1b17e273b64af13040c064c9ed62c8b32fba", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/fa4e0d3a918db0eab8e5c5be952f3bd649968456", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -15,24 +15,21 @@ "include": "#statements" }, { - "name": "comment.line.shebang.ts", + "include": "#shebang" + } + ], + "repository": { + "shebang": { + "name": "comment.line.shebang.js", "match": "\\A(#!).*(?=$)", "captures": { "1": { - "name": "punctuation.definition.comment.ts" + "name": "punctuation.definition.comment.js" } } - } - ], - "repository": { + }, "statements": { "patterns": [ - { - "include": "#string" - }, - { - "include": "#comment" - }, { "include": "#declaration" }, @@ -53,6 +50,12 @@ }, { "include": "#punctuation-semicolon" + }, + { + "include": "#string" + }, + { + "include": "#comment" } ] }, @@ -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" @@ -1081,13 +1084,13 @@ }, "field-declaration": { "name": "meta.field.declaration.js", - "begin": "(?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)(\\#?[_$[: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" @@ -1123,7 +1126,7 @@ }, { "name": "meta.definition.property.js variable.object.property.js", - "match": "[_$[:alpha:]][_$[:alnum:]]*" + "match": "\\#?[_$[:alpha:]][_$[:alnum:]]*" }, { "name": "keyword.operator.optional.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" @@ -1825,7 +1828,7 @@ }, "access-modifier": { "name": "storage.modifier.js", - "match": "(?)\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" @@ -2673,7 +2691,7 @@ "name": "keyword.control.as.js" } }, - "end": "(?=$|^|[,}]|\\|\\||\\&\\&|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?)", + "patterns": [ + { + "include": "#type-parameters" + } + ] + }, + { + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "beginCaptures": { + "1": { + "name": "meta.brace.round.js" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "meta.brace.round.js" + } + }, + "patterns": [ + { + "include": "#expression-inside-possibly-arrow-parens" + } + ] + }, { "include": "#possibly-arrow-return-type" }, @@ -2788,26 +2839,75 @@ ] }, "function-call": { - "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { - "name": "meta.function-call.js", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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": "\\?\\." @@ -2815,12 +2915,6 @@ { "name": "meta.function-call.js keyword.operator.definiteassignment.js", "match": "\\!" - }, - { - "include": "#type-arguments" - }, - { - "include": "#paren-expression" } ] }, @@ -2852,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" @@ -2986,7 +3080,7 @@ } }, { - "match": "(?x)(?:(?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?=|<>|<|>" }, + { + "match": "(?<=[_$[:alnum:]])(\\!)\\s*(/)(?![/*])", + "captures": { + "1": { + "name": "keyword.operator.logical.js" + }, + "2": { + "name": "keyword.operator.arithmetic.js" + } + } + }, { "name": "keyword.operator.logical.js", "match": "\\!|&&|\\|\\||\\?\\?" @@ -3427,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" @@ -3582,7 +3642,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3596,7 +3656,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3626,7 +3686,7 @@ "match": "([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\\??\\.\\s*prototype\\b(?!\\$))" }, { - "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n (\\#?[[:upper:]][_$[:digit:][:upper:]]*) |\n (\\#?[_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3643,7 +3703,7 @@ } }, { - "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "variable.other.constant.object.js" @@ -3962,6 +4022,24 @@ } }, "patterns": [ + { + "name": "keyword.operator.rest.js", + "match": "\\.\\.\\." + }, + { + "match": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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" @@ -4506,7 +4588,7 @@ }, { "name": "string.template.js", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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" @@ -4572,7 +4654,7 @@ "patterns": [ { "name": "string.regexp.js", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js" @@ -4595,7 +4677,7 @@ }, { "name": "string.regexp.js", - "begin": "((?)\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" @@ -1081,13 +1084,13 @@ }, "field-declaration": { "name": "meta.field.declaration.js.jsx", - "begin": "(?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)(\\#?[_$[: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" @@ -1123,7 +1126,7 @@ }, { "name": "meta.definition.property.js.jsx variable.object.property.js.jsx", - "match": "[_$[:alpha:]][_$[:alnum:]]*" + "match": "\\#?[_$[:alpha:]][_$[:alnum:]]*" }, { "name": "keyword.operator.optional.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" @@ -1825,7 +1828,7 @@ }, "access-modifier": { "name": "storage.modifier.js.jsx", - "match": "(?)\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" @@ -2673,7 +2691,7 @@ "name": "keyword.control.as.js.jsx" } }, - "end": "(?=$|^|[,}]|\\|\\||\\&\\&|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?)", + "patterns": [ + { + "include": "#type-parameters" + } + ] + }, + { + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "beginCaptures": { + "1": { + "name": "meta.brace.round.js.jsx" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "meta.brace.round.js.jsx" + } + }, + "patterns": [ + { + "include": "#expression-inside-possibly-arrow-parens" + } + ] + }, { "include": "#possibly-arrow-return-type" }, @@ -2788,26 +2839,75 @@ ] }, "function-call": { - "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { - "name": "meta.function-call.js.jsx", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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": "\\?\\." @@ -2815,12 +2915,6 @@ { "name": "meta.function-call.js.jsx keyword.operator.definiteassignment.js.jsx", "match": "\\!" - }, - { - "include": "#type-arguments" - }, - { - "include": "#paren-expression" } ] }, @@ -2852,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" @@ -2986,7 +3080,7 @@ } }, { - "match": "(?x)(?:(?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?=|<>|<|>" }, + { + "match": "(?<=[_$[:alnum:]])(\\!)\\s*(/)(?![/*])", + "captures": { + "1": { + "name": "keyword.operator.logical.js.jsx" + }, + "2": { + "name": "keyword.operator.arithmetic.js.jsx" + } + } + }, { "name": "keyword.operator.logical.js.jsx", "match": "\\!|&&|\\|\\||\\?\\?" @@ -3427,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" @@ -3582,7 +3642,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3596,7 +3656,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3626,7 +3686,7 @@ "match": "([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\\??\\.\\s*prototype\\b(?!\\$))" }, { - "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n (\\#?[[:upper:]][_$[:digit:][:upper:]]*) |\n (\\#?[_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3643,7 +3703,7 @@ } }, { - "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "variable.other.constant.object.js.jsx" @@ -3962,6 +4022,24 @@ } }, "patterns": [ + { + "name": "keyword.operator.rest.js.jsx", + "match": "\\.\\.\\." + }, + { + "match": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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" @@ -4506,7 +4588,7 @@ }, { "name": "string.template.js.jsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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" @@ -4572,7 +4654,7 @@ "patterns": [ { "name": "string.regexp.js.jsx", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js.jsx" @@ -4595,7 +4677,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "((? { + 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 0000000000000..a2ff8c1b78a93 --- /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: 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(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 d7c6684b3f2dd..0000000000000 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ /dev/null @@ -1,426 +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, Disposable } from 'vscode'; -import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, DocumentRangeFormattingRequest } 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'); -} - -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. - }, - 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); - } - } - }; - - // 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())); - }); - - 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 settings: Settings = { - http: { - proxy: httpSettings.get('proxy'), - proxyStrictSSL: httpSettings.get('proxyStrictSSL') - }, - json: { - schemas: [], - resultLimit: 5000 - } - }; - 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 0000000000000..6e9aed7f105dc --- /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 0000000000000..be05fae6aef92 --- /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 0000000000000..900ef6a3b1243 --- /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 a4d4ff955d70e..13a0595299561 100644 --- a/extensions/json-language-features/extension.webpack.config.js +++ b/extensions/json-language-features/extension.webpack.config.js @@ -9,20 +9,20 @@ const withDefaults = require('../shared.webpack.config'); const path = require('path'); -var webpack = require('webpack'); +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') } }); // add plugin, don't replace inherited config.plugins.push(new webpack.IgnorePlugin(/vertx/)); // request-light dependency -module.exports = config; \ No newline at end of file +module.exports = config; diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 833059f3c839b..db003f051216e 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -1,113 +1,138 @@ { - "name": "json-language-features", - "displayName": "%displayName%", - "description": "%description%", - "version": "1.0.0", - "publisher": "vscode", - "license": "MIT", - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", - "engines": { - "vscode": "0.10.x" - }, - "icon": "icons/json.png", - "activationEvents": [ - "onLanguage:json", - "onLanguage:jsonc" - ], - "main": "./client/out/jsonMain", - "enableProposedApi": true, - "scripts": { - "compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server", - "watch": "gulp watch-extension:json-language-features-client watch-extension:json-language-features-server", - "postinstall": "cd server && yarn install", - "install-client-next": "yarn add vscode-languageclient@next" - }, - "categories": [ - "Programming Languages" - ], - "contributes": { - "configuration": { - "id": "json", - "order": 20, - "type": "object", - "title": "JSON", - "properties": { - "json.schemas": { - "type": "array", - "scope": "resource", - "description": "%json.schemas.desc%", - "items": { + "name": "json-language-features", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.0", + "publisher": "vscode", + "license": "MIT", + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "engines": { + "vscode": "0.10.x" + }, + "icon": "icons/json.png", + "activationEvents": [ + "onLanguage:json", + "onLanguage:jsonc" + ], + "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", + "watch": "gulp watch-extension:json-language-features-client watch-extension:json-language-features-server", + "postinstall": "cd server && yarn install", + "install-client-next": "yarn add vscode-languageclient@next" + }, + "categories": [ + "Programming Languages" + ], + "contributes": { + "configuration": { + "id": "json", + "order": 20, "type": "object", - "default": { - "fileMatch": [ - "/myfile" - ], - "url": "schemaURL" - }, + "title": "JSON", "properties": { - "url": { - "type": "string", - "default": "/user.schema.json", - "description": "%json.schemas.url.desc%" - }, - "fileMatch": { - "type": "array", - "items": { - "type": "string", - "default": "MyFile.json", - "description": "%json.schemas.fileMatch.item.desc%" + "json.schemas": { + "type": "array", + "scope": "resource", + "description": "%json.schemas.desc%", + "items": { + "type": "object", + "default": { + "fileMatch": [ + "/myfile" + ], + "url": "schemaURL" + }, + "properties": { + "url": { + "type": "string", + "default": "/user.schema.json", + "description": "%json.schemas.url.desc%" + }, + "fileMatch": { + "type": "array", + "items": { + "type": "string", + "default": "MyFile.json", + "description": "%json.schemas.fileMatch.item.desc%" + }, + "minItems": 1, + "description": "%json.schemas.fileMatch.desc%" + }, + "schema": { + "$ref": "http://json-schema.org/draft-07/schema#", + "description": "%json.schemas.schema.desc%" + } + } + } + }, + "json.format.enable": { + "type": "boolean", + "scope": "window", + "default": true, + "description": "%json.format.enable.desc%" + }, + "json.trace.server": { + "type": "string", + "scope": "window", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "%json.tracing.desc%" + }, + "json.colorDecorators.enable": { + "type": "boolean", + "scope": "window", + "default": true, + "description": "%json.colorDecorators.enable.desc%", + "deprecationMessage": "%json.colorDecorators.enable.deprecationMessage%" + }, + "json.maxItemsComputed": { + "type": "number", + "default": 5000, + "description": "%json.maxItemsComputed.desc%" }, - "minItems": 1, - "description": "%json.schemas.fileMatch.desc%" - }, - "schema": { - "$ref": "http://json-schema.org/draft-07/schema#", - "description": "%json.schemas.schema.desc%" + "json.schemaDownload.enable": { + "type": "boolean", + "default": true, + "description": "%json.enableSchemaDownload.desc%", + "tags": ["usesOnlineServices"] } } - } }, - "json.format.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%json.format.enable.desc%" - }, - "json.trace.server": { - "type": "string", - "scope": "window", - "enum": [ - "off", - "messages", - "verbose" - ], - "default": "off", - "description": "%json.tracing.desc%" + "configurationDefaults": { + "[json]": { + "editor.quickSuggestions": { + "strings": true + }, + "editor.suggest.insertMode": "replace" + }, + "[jsonc]": { + "editor.quickSuggestions": { + "strings": true + }, + "editor.suggest.insertMode": "replace" + } }, - "json.colorDecorators.enable": { - "type": "boolean", - "scope": "window", - "default": true, - "description": "%json.colorDecorators.enable.desc%", - "deprecationMessage": "%json.colorDecorators.enable.deprecationMessage%" - } - } + "jsonValidation": [ + { + "fileMatch": "*.schema.json", + "url": "http://json-schema.org/draft-07/schema#" + } + ] + }, + "dependencies": { + "request-light": "^0.3.0", + "vscode-extension-telemetry": "0.1.1", + "vscode-languageclient": "7.0.0-next.5.1", + "vscode-nls": "^4.1.2" }, - "configurationDefaults": { - "[json]": { - "editor.quickSuggestions": { - "strings": true - } - } + "devDependencies": { + "@types/node": "^12.11.7" } - }, - "dependencies": { - "request-light": "^0.2.5", - "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^6.0.0-next.3", - "vscode-nls": "^4.1.1" - }, - "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 c61e7b70e8f5c..59729a0ee9922 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", @@ -11,5 +11,7 @@ "json.colorDecorators.enable.desc": "Enables or disables color decorators", "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.clickToRetry": "Click to retry.", + "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 71356e3c4415e..a399a8d223c40 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/). @@ -46,6 +47,8 @@ The client can send the following initialization options to the server: - `provideFormatter: boolean | undefined`. If defined, the value defines whether the server provides the `documentRangeFormattingProvider` capability on initialization. If undefined, the setting `json.format.enable` is used to determine whether formatting is provided. The formatter will then be registered through dynamic registration. If the client does not support dynamic registration, no formatter will be available. - `handledSchemaProtocols`: The URI schemas handles by the server. See section `Schema configuration` below. +- `customCapabilities`: Additional non-LSP client capabilities: + - `rangeFormatting: { editLimit: x } }`: For performance reasons, limit the number of edits returned by the range formatter to `x`. ### Settings @@ -59,13 +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. + - `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 @@ -82,7 +86,7 @@ The server supports the following settings: ], "url": "http://json.schemastore.org/foo", "schema": { - "type": "array" + "type": "array" } } ] @@ -139,13 +143,35 @@ 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[]; + +} + ``` +`ISchemaAssociations` - keys: a file names or file path (separated by `/`). `*` can be used as a wildcard. - values: An array of schema URLs @@ -153,6 +179,16 @@ Notification: - method: 'json/schemaContent' - params: `string` the URL of the schema that has changed. +### 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 can be shown to the user. + +Notification: +- method: 'json/resultLimitReached' +- params: a human readable string to show to the user. + + ## Try The JSON language server is shipped with [Visual Studio Code](https://code.visualstudio.com/) as part of the built-in VSCode extension `json-language-features`. The server is started when the first JSON file is opened. The [VSCode JSON documentation](https://code.visualstudio.com/docs/languages/json) for detailed information on the user experience and has more information on how to configure the language support. @@ -166,7 +202,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 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 0000000000000..b1ae74c7b6060 --- /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 22b23c1d94bc2..50a7c3d545f00 100644 --- a/extensions/json-language-features/server/extension.webpack.config.js +++ b/extensions/json-language-features/server/extension.webpack.config.js @@ -9,16 +9,16 @@ const withDefaults = require('../../shared.webpack.config'); const path = require('path'); -var webpack = require('webpack'); +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 6dc132cc742af..ddf75c333e619 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.2.3", "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.7", - "vscode-languageserver": "^6.0.0-next.3", - "vscode-uri": "^2.1.1" + "jsonc-parser": "^2.2.1", + "request-light": "^0.3.0", + "vscode-json-languageservice": "^3.8.3", + "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 0000000000000..5394412877c81 --- /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 0000000000000..761e638a9f3d9 --- /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, + definitionProvider: 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; + } + + + 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.onDefinition((params, token) => { + return runSafeAsync(async () => { + const document = documents.get(params.textDocument.uri); + if (document) { + const jsonDocument = getJSONDocument(document); + return languageService.findDefinition(document, params.position, jsonDocument); + } + return []; + }, [], `Error while computing definitions 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 975f27b481dac..0000000000000 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ /dev/null @@ -1,497 +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 -} 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 } 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 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; - -// 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 && 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); - 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.window.showInformationMessage(`${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) { - return languageService.format(document, formatParams.range, formatParams.options); - } - 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 0000000000000..94192b4b1ce48 --- /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 0000000000000..a87cf92483f85 --- /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 f0fde79ab0cbe..ac7354a958e43 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,77 +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== 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.7: - version "3.4.7" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.7.tgz#8d85f3c1d46a1e58e9867d747552fb8c83d934fd" - integrity sha512-y3MN2+/yph3yoIHGmHu4ScYpm285L58XVvfGkd49xTQzLja4apxSbwzsYcP9QsqS0W7KuvoyiPhqksiudoMwjg== +vscode-json-languageservice@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.8.3.tgz#fae5e7bdda2b6ec4f64588c571df40b6bfcb09b5" + integrity sha512-8yPag/NQHCuTthahyaTtzK0DHT0FKM/xBU0mFBQ8nMo8C1i2P+FCyIVqICoNoHkRI2BTGlXKomPUpsqjSz0TnQ== dependencies: - jsonc-parser "^2.2.0" - 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.0" - -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== + jsonc-parser "^2.2.1" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "^3.15.1" + vscode-nls "^4.1.2" + vscode-uri "^2.1.2" + +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.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-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-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-jsonrpc "6.0.0-next.2" + vscode-languageserver-types "3.16.0-next.2" + +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.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-types@^3.15.1: + version "3.15.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" + integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ== + +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.0-next.10" + 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.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.0.tgz#475a4269e63edbc13914b40c84bc1416e3398156" - integrity sha512-3voe44nOhb6OdKlpZShVsmVvY2vFQHMe6REP3Ky9RVJuPyM/XidsjH6HncCIDdSmbcF5YQHrTC/Q+Q2loJGkOw== +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== -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-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 27e4f7cf883d0..29972b7df0394 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.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" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" diff --git a/extensions/json/package.json b/extensions/json/package.json index e66955770bce3..73902a8ec49f1 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" } @@ -67,12 +71,6 @@ "scopeName": "source.json.comments", "path": "./syntaxes/JSONC.tmLanguage.json" } - ], - "jsonValidation": [ - { - "fileMatch": "*.schema.json", - "url": "http://json-schema.org/draft-07/schema#" - } ] } } diff --git a/extensions/json/test/colorize-results/test_json.json b/extensions/json/test/colorize-results/test_json.json index 75561c366d70a..6f94bec76e6ea 100644 --- a/extensions/json/test/colorize-results/test_json.json +++ b/extensions/json/test/colorize-results/test_json.json @@ -279,9 +279,9 @@ "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json constant.numeric.json", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -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" @@ -488,9 +488,9 @@ "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json constant.numeric.json", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -686,9 +686,9 @@ "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json constant.numeric.json", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, diff --git a/extensions/less/test/colorize-results/test-cssvariables_less.json b/extensions/less/test/colorize-results/test-cssvariables_less.json index 1c9b8658a0adf..41db8bdaf48ab 100644 --- a/extensions/less/test/colorize-results/test-cssvariables_less.json +++ b/extensions/less/test/colorize-results/test-cssvariables_less.json @@ -103,9 +103,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -114,9 +114,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -202,9 +202,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -422,9 +422,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -521,9 +521,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css meta.function.variable.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -532,9 +532,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css meta.function.variable.css constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -582,4 +582,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/less/test/colorize-results/test_less.json b/extensions/less/test/colorize-results/test_less.json index d6b39257feb6b..7c48bc0681e90 100644 --- a/extensions/less/test/colorize-results/test_less.json +++ b/extensions/less/test/colorize-results/test_less.json @@ -829,9 +829,9 @@ "t": "source.css.less constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -840,9 +840,9 @@ "t": "source.css.less constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1082,9 +1082,9 @@ "t": "source.css.less meta.property-list.css meta.function.color.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1115,9 +1115,9 @@ "t": "source.css.less meta.property-list.css meta.function.color.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1148,9 +1148,9 @@ "t": "source.css.less meta.property-list.css meta.function.color.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1390,9 +1390,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1401,9 +1401,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1544,9 +1544,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1555,9 +1555,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1665,9 +1665,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1687,9 +1687,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1709,9 +1709,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1720,9 +1720,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1764,9 +1764,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1775,9 +1775,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1973,9 +1973,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1984,9 +1984,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css meta.property-value.css constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -2182,9 +2182,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2193,9 +2193,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css meta.property-value.css constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -2435,9 +2435,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css meta.property-list.css meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2446,9 +2446,9 @@ "t": "source.css.less meta.property-list.css meta.property-list.css meta.property-list.css meta.property-list.css meta.property-value.css constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -2578,9 +2578,9 @@ "t": "source.css.less constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2589,9 +2589,9 @@ "t": "source.css.less constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -2897,9 +2897,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3117,9 +3117,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3458,9 +3458,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3469,9 +3469,9 @@ "t": "source.css.less meta.property-list.css meta.property-value.css constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -3508,4 +3508,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/log/cgmanifest.json b/extensions/log/cgmanifest.json index 56318ba0e84d7..0fe21e5c2c4e0 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 b0f144a67fe62..4882a761e6f71 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/lua/language-configuration.json b/extensions/lua/language-configuration.json index 89e5c45b9ff01..5e5e0ab447706 100644 --- a/extensions/lua/language-configuration.json +++ b/extensions/lua/language-configuration.json @@ -12,8 +12,8 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], diff --git a/extensions/lua/test/colorize-results/test_lua.json b/extensions/lua/test/colorize-results/test_lua.json index 21c3d794e5b81..16117e5a94a73 100644 --- a/extensions/lua/test/colorize-results/test_lua.json +++ b/extensions/lua/test/colorize-results/test_lua.json @@ -180,9 +180,9 @@ "t": "source.lua constant.numeric.integer.lua", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -246,9 +246,9 @@ "t": "source.lua constant.numeric.integer.lua", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -367,9 +367,9 @@ "t": "source.lua constant.numeric.integer.lua", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -681,4 +681,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/make/cgmanifest.json b/extensions/make/cgmanifest.json index 544b0bc5bd3e8..9a311cfe811e6 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 2503fce46fc45..890148cb19f9f 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 45a7c80465a27..7720e43ae4d05 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 616de8fb8e086..eacaa05384ff1 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 26a3599cf5bad..bbc5e342db0b6 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.code-snippets b/extensions/markdown-basics/snippets/markdown.code-snippets new file mode 100644 index 0000000000000..b07f984138cc7 --- /dev/null +++ b/extensions/markdown-basics/snippets/markdown.code-snippets @@ -0,0 +1,87 @@ +{ + "Insert bold text": { + "prefix": "bold", + "body": "**${1:${TM_SELECTED_TEXT}}**$0", + "description": "Insert bold text" + }, + "Insert italic text": { + "prefix": "italic", + "body": "*${1:${TM_SELECTED_TEXT}}*$0", + "description": "Insert italic text" + }, + "Insert quoted text": { + "prefix": "quote", + "body": "> ${1:${TM_SELECTED_TEXT}}", + "description": "Insert quoted text" + }, + "Insert inline code": { + "prefix": "code", + "body": "`${1:${TM_SELECTED_TEXT}}`$0", + "description": "Insert inline code" + }, + "Insert fenced code block": { + "prefix": "fenced codeblock", + "body": ["```${1:language}", "${TM_SELECTED_TEXT}$0", "```"], + "description": "Insert fenced code block" + }, + "Insert heading level 1": { + "prefix": "heading1", + "body": "# ${1:${TM_SELECTED_TEXT}}", + "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"], + "description": "Insert unordered list" + }, + "Insert ordered list": { + "prefix": "ordered list", + "body": ["1. ${1:first}", "2. ${2:second}", "3. ${3:third}", "$0"], + "description": "Insert ordered list" + }, + "Insert horizontal rule": { + "prefix": "horizontal rule", + "body": "----------\n", + "description": "Insert horizontal rule" + }, + "Insert link": { + "prefix": "link", + "body": "[${TM_SELECTED_TEXT:${1:text}}](https://${2:link})$0", + "description": "Insert link" + }, + "Insert image": { + "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/snippets/markdown.json b/extensions/markdown-basics/snippets/markdown.json deleted file mode 100644 index 6ee831ae4a52b..0000000000000 --- a/extensions/markdown-basics/snippets/markdown.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "Insert bold text": { - "prefix": "bold", - "body": "**${1:${TM_SELECTED_TEXT}}**$0", - "description": "Insert bold text" - }, - "Insert italic text": { - "prefix": "italic", - "body": "*${1:${TM_SELECTED_TEXT}}*$0", - "description": "Insert italic text" - }, - "Insert quoted text": { - "prefix": "quote", - "body": "> ${1:${TM_SELECTED_TEXT}}", - "description": "Insert quoted text" - }, - "Insert code": { - "prefix": "code", - "body": "`${1:${TM_SELECTED_TEXT}}`$0", - "description": "Insert code" - }, - "Insert fenced code block": { - "prefix": "fenced codeblock", - "body": [ - "```${1:language}", - "${TM_SELECTED_TEXT}$0", - "```" - ], - "description": "Insert fenced code block" - }, - "Insert heading": { - "prefix": "heading", - "body": "# ${1:${TM_SELECTED_TEXT}}", - "description": "Insert heading" - }, - "Insert unordered list": { - "prefix": "unordered list", - "body": [ - "- ${1:first}", - "- ${2:second}", - "- ${3:third}", - "$0" - ], - "description": "Insert unordered list" - }, - "Insert ordered list": { - "prefix": "ordered list", - "body": [ - "1. ${1:first}", - "2. ${2:second}", - "3. ${3:third}", - "$0" - ], - "description": "Insert ordered list" - }, - "Insert horizontal rule": { - "prefix": "horizontal rule", - "body": "----------\n", - "description": "Insert horizontal rule" - }, - "Insert link": { - "prefix": "link", - "body": "[${TM_SELECTED_TEXT:${1:text}}](https://${2:link})$0", - "description": "Insert link" - }, - "Insert image": { - "prefix": "image", - "body": "![${TM_SELECTED_TEXT:${1:alt}}](https://${2:link})$0", - "description": "Insert image" - } -} diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 0e2578d4bddfb..e8feaa75fa667 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/e3091a421bdcad527018c897652ded47585cbd12", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/4be9cb335581f3559166c319607dac9100103083", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -957,7 +957,7 @@ ] }, "fenced_code_block_js": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(js|jsx|javascript|es6|mjs|\\{\\.js.+?\\})((\\s+|:|\\{)[^`~]*)?$)", + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(js|jsx|javascript|es6|mjs|cjs|\\{\\.js.+?\\})((\\s+|:|\\{)[^`~]*)?$)", "name": "markup.fenced_code.block.markdown", "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", "beginCaptures": { @@ -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 9bc9aa7595f75..1fee128741772 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", @@ -1134,7 +1156,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html source.css", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html source.css-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -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 30d948fbc6610..9f1e062077520 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 0000000000000..7fcc53a728b4f --- /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 5e5314ae72558..0433f33c89a62 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 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=2)}([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,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(0);function i(e){return t=0,n=o.getSettings().lineCount-1,i=e,Math.min(n,Math.max(t,i));var t,n,i}const r=(()=>{let e;return()=>{if(!e){e=[{element:document.body,line:0}];for(const t of document.getElementsByClassName("code-line")){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=r();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=r(),n=e-window.scrollY;let o=-1,i=t.length-1;for(;o+1<i;){const e=Math.floor((o+i)/2),r=t[e].element.getBoundingClientRect();r.top+r.height>=n?i=e:o=e}const s=t[i],c=s.element.getBoundingClientRect();if(i>=1&&c.top>n){return{previous:t[o],next:s}}return i>1&&i<t.length&&c.top+c.height>n?{previous:s,next:t[i+1]}:{previous:s}}t.getElementsForSourceLine=s,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}=s(e);if(!t)return;let i=0;const r=t.element.getBoundingClientRect(),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}=c(e);if(t){const o=t.element.getBoundingClientRect(),r=e-window.scrollY-o.top;if(n){const e=r/(n.element.getBoundingClientRect().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 r().find(t=>t.element.id===e)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});const o=n(3),i=n(4),r=n(5),s=n(1),c=n(0),a=n(6);let u=!0;const l=new o.ActiveLineMarker,d=c.getSettings(),f=acquireVsCodeApi();let g=c.getData("data-state");f.setState(g);const p=r.createPosterForVsCode(f);window.cspAlerter.setPoster(p),window.styleLoadingMonitor.setPoster(p),window.onload=()=>{v()},i.onceDocumentLoaded(()=>{d.scrollPreviewWithEditor&&setTimeout(()=>{if(g.fragment){const e=s.getLineElementForFragment(g.fragment);e&&(u=!0,s.scrollToRevealSourceLine(e.line))}else{const e=+d.line;isNaN(e)||(u=!0,s.scrollToRevealSourceLine(e))}},0)});const m=(()=>{const e=a(e=>{u=!0,s.scrollToRevealSourceLine(e)},50);return(t,n)=>{isNaN(t)||(n.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})}p.postMessage("cacheImageSizes",e)}},50);window.addEventListener("resize",()=>{u=!0,v()},!0),window.addEventListener("message",e=>{if(e.data.source===d.source)switch(e.data.type){case"onDidChangeTextEditorSelection":l.onDidChangeTextEditorSelection(e.data.line);break;case"updateView":m(e.data.line,d)}},!1),document.addEventListener("dblclick",e=>{if(!d.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",a(()=>{if(u)u=!1;else{const e=s.getEditorLineNumberForPageOffset(window.scrollY);"number"!=typeof e||isNaN(e)||(p.postMessage("revealLine",{line:e}),g.line=e,f.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,i="[object Symbol]",r=/^\s+|\s+$/g,s=/^[-+]0x[0-9a-f]+$/i,c=/^0b[01]+$/i,a=/^0o[0-7]+$/i,u=parseInt,l="object"==typeof t&&t&&t.Object===Object&&t,d="object"==typeof self&&self&&self.Object===Object&&self,f=l||d||Function("return this")(),g=Object.prototype.toString,p=Math.max,m=Math.min,v=function(){return f.Date.now()};function h(e,t,o){var i,r,s,c,a,u,l=0,d=!1,f=!1,g=!0;if("function"!=typeof e)throw new TypeError(n);function h(t){var n=i,o=r;return i=r=void 0,l=t,c=e.apply(o,n)}function y(e){var n=e-u;return void 0===u||n>=t||n<0||f&&e-l>=s}function E(){var e=v();if(y(e))return M(e);a=setTimeout(E,function(e){var n=t-(e-u);return f?m(n,s-(e-l)):n}(e))}function M(e){return a=void 0,g&&i?h(e):(i=r=void 0,c)}function L(){var e=v(),n=y(e);if(i=arguments,r=this,u=e,n){if(void 0===a)return function(e){return l=e,a=setTimeout(E,t),d?h(e):c}(u);if(f)return a=setTimeout(E,t),h(u)}return void 0===a&&(a=setTimeout(E,t)),c}return t=b(t)||0,w(o)&&(d=!!o.leading,s=(f="maxWait"in o)?p(b(o.maxWait)||0,t):s,g="trailing"in o?!!o.trailing:g),L.cancel=function(){void 0!==a&&clearTimeout(a),l=0,i=u=r=a=void 0},L.flush=function(){return void 0===a?c:M(v())},L}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)==i}(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(r,"");var n=c.test(e);return n||a.test(e)?u(e.slice(2),n?2:8):s.test(e)?o:+e}e.exports=function(e,t,o){var i=!0,r=!0;if("function"!=typeof e)throw new TypeError(n);return w(o)&&(i="leading"in o?!!o.leading:i,r="trailing"in o?!!o.trailing:r),h(e,t,{leading:i,maxWait:t,trailing:r})}}).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,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2V0dGluZ3MudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvc2Nyb2xsLXN5bmMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvaW5kZXgudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvYWN0aXZlTGluZU1hcmtlci50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9ldmVudHMudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvbWVzc2FnaW5nLnRzIiwid2VicGFjazovLy8uL25vZGVfbW9kdWxlcy9sb2Rhc2gudGhyb3R0bGUvaW5kZXguanMiLCJ3ZWJwYWNrOi8vLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyJdLCJuYW1lcyI6WyJpbnN0YWxsZWRNb2R1bGVzIiwiX193ZWJwYWNrX3JlcXVpcmVfXyIsIm1vZHVsZUlkIiwiZXhwb3J0cyIsIm1vZHVsZSIsImkiLCJsIiwibW9kdWxlcyIsImNhbGwiLCJtIiwiYyIsImQiLCJuYW1lIiwiZ2V0dGVyIiwibyIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiZW51bWVyYWJsZSIsImdldCIsInIiLCJTeW1ib2wiLCJ0b1N0cmluZ1RhZyIsInZhbHVlIiwidCIsIm1vZGUiLCJfX2VzTW9kdWxlIiwibnMiLCJjcmVhdGUiLCJrZXkiLCJiaW5kIiwibiIsIm9iamVjdCIsInByb3BlcnR5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJwIiwicyIsImNhY2hlZFNldHRpbmdzIiwidW5kZWZpbmVkIiwiZ2V0RGF0YSIsImVsZW1lbnQiLCJkb2N1bWVudCIsImdldEVsZW1lbnRCeUlkIiwiZGF0YSIsImdldEF0dHJpYnV0ZSIsIkpTT04iLCJwYXJzZSIsIkVycm9yIiwiZ2V0U2V0dGluZ3MiLCJzZXR0aW5nc18xIiwiY2xhbXBMaW5lIiwibGluZSIsIm1pbiIsIm1heCIsImxpbmVDb3VudCIsIk1hdGgiLCJnZXRDb2RlTGluZUVsZW1lbnRzIiwiZWxlbWVudHMiLCJib2R5IiwiZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSIsImlzTmFOIiwidGFnTmFtZSIsInBhcmVudEVsZW1lbnQiLCJwdXNoIiwiZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lIiwidGFyZ2V0TGluZSIsImxpbmVOdW1iZXIiLCJmbG9vciIsImxpbmVzIiwicHJldmlvdXMiLCJlbnRyeSIsIm5leHQiLCJnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQiLCJvZmZzZXQiLCJwb3NpdGlvbiIsIndpbmRvdyIsInNjcm9sbFkiLCJsbyIsImhpIiwibGVuZ3RoIiwibWlkIiwiYm91bmRzIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidG9wIiwiaGVpZ2h0IiwiaGlFbGVtZW50IiwiaGlCb3VuZHMiLCJzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUiLCJzY3JvbGxQcmV2aWV3V2l0aEVkaXRvciIsInNjcm9sbCIsInNjcm9sbFgiLCJzY3JvbGxUbyIsInJlY3QiLCJwcmV2aW91c1RvcCIsInByb2dyZXNzSW5FbGVtZW50IiwiZ2V0RWRpdG9yTGluZU51bWJlckZvclBhZ2VPZmZzZXQiLCJwcmV2aW91c0JvdW5kcyIsIm9mZnNldEZyb21QcmV2aW91cyIsInByb2dyZXNzQmV0d2VlbkVsZW1lbnRzIiwicHJvZ3Jlc3NXaXRoaW5FbGVtZW50IiwiZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCIsImZyYWdtZW50IiwiZmluZCIsImlkIiwiYWN0aXZlTGluZU1hcmtlcl8xIiwiZXZlbnRzXzEiLCJtZXNzYWdpbmdfMSIsInNjcm9sbF9zeW5jXzEiLCJ0aHJvdHRsZSIsInNjcm9sbERpc2FibGVkIiwibWFya2VyIiwiQWN0aXZlTGluZU1hcmtlciIsInNldHRpbmdzIiwidnNjb2RlIiwiYWNxdWlyZVZzQ29kZUFwaSIsInN0YXRlIiwic2V0U3RhdGUiLCJtZXNzYWdpbmciLCJjcmVhdGVQb3N0ZXJGb3JWc0NvZGUiLCJjc3BBbGVydGVyIiwic2V0UG9zdGVyIiwic3R5bGVMb2FkaW5nTW9uaXRvciIsIm9ubG9hZCIsInVwZGF0ZUltYWdlU2l6ZXMiLCJvbmNlRG9jdW1lbnRMb2FkZWQiLCJzZXRUaW1lb3V0IiwiaW5pdGlhbExpbmUiLCJvblVwZGF0ZVZpZXciLCJkb1Njcm9sbCIsImltYWdlSW5mbyIsImltYWdlcyIsImdldEVsZW1lbnRzQnlUYWdOYW1lIiwiaW1nIiwiY2xhc3NMaXN0IiwiY29udGFpbnMiLCJyZW1vdmUiLCJ3aWR0aCIsInBvc3RNZXNzYWdlIiwiYWRkRXZlbnRMaXN0ZW5lciIsImV2ZW50Iiwic291cmNlIiwidHlwZSIsIm9uRGlkQ2hhbmdlVGV4dEVkaXRvclNlbGVjdGlvbiIsImRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvciIsIm5vZGUiLCJ0YXJnZXQiLCJwYXJlbnROb2RlIiwicGFnZVkiLCJwYXNzVGhyb3VnaExpbmtTY2hlbWVzIiwiaHJlZiIsInN0YXJ0c1dpdGgiLCJzb21lIiwic2NoZW1lIiwiaHJlZlRleHQiLCJ0ZXN0IiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iLCJ0aGlzIiwiX3VwZGF0ZSIsImJlZm9yZSIsIl91bm1hcmtBY3RpdmVFbGVtZW50IiwiX2N1cnJlbnQiLCJfbWFya0FjdGl2ZUVsZW1lbnQiLCJjbGFzc05hbWUiLCJyZXBsYWNlIiwiZiIsInJlYWR5U3RhdGUiLCJGVU5DX0VSUk9SX1RFWFQiLCJOQU4iLCJzeW1ib2xUYWciLCJyZVRyaW0iLCJyZUlzQmFkSGV4IiwicmVJc0JpbmFyeSIsInJlSXNPY3RhbCIsImZyZWVQYXJzZUludCIsInBhcnNlSW50IiwiZnJlZUdsb2JhbCIsImdsb2JhbCIsImZyZWVTZWxmIiwic2VsZiIsInJvb3QiLCJGdW5jdGlvbiIsIm9iamVjdFRvU3RyaW5nIiwidG9TdHJpbmciLCJuYXRpdmVNYXgiLCJuYXRpdmVNaW4iLCJub3ciLCJEYXRlIiwiZGVib3VuY2UiLCJmdW5jIiwid2FpdCIsIm9wdGlvbnMiLCJsYXN0QXJncyIsImxhc3RUaGlzIiwibWF4V2FpdCIsInJlc3VsdCIsInRpbWVySWQiLCJsYXN0Q2FsbFRpbWUiLCJsYXN0SW52b2tlVGltZSIsImxlYWRpbmciLCJtYXhpbmciLCJ0cmFpbGluZyIsIlR5cGVFcnJvciIsImludm9rZUZ1bmMiLCJ0aW1lIiwiYXJncyIsInRoaXNBcmciLCJhcHBseSIsInNob3VsZEludm9rZSIsInRpbWVTaW5jZUxhc3RDYWxsIiwidGltZXJFeHBpcmVkIiwidHJhaWxpbmdFZGdlIiwicmVtYWluaW5nV2FpdCIsImRlYm91bmNlZCIsImlzSW52b2tpbmciLCJhcmd1bWVudHMiLCJsZWFkaW5nRWRnZSIsInRvTnVtYmVyIiwiaXNPYmplY3QiLCJjYW5jZWwiLCJjbGVhclRpbWVvdXQiLCJmbHVzaCIsImlzT2JqZWN0TGlrZSIsImlzU3ltYm9sIiwib3RoZXIiLCJ2YWx1ZU9mIiwiaXNCaW5hcnkiLCJzbGljZSIsImciLCJlIl0sIm1hcHBpbmdzIjoiYUFDRSxJQUFJQSxFQUFtQixHQUd2QixTQUFTQyxFQUFvQkMsR0FHNUIsR0FBR0YsRUFBaUJFLEdBQ25CLE9BQU9GLEVBQWlCRSxHQUFVQyxRQUduQyxJQUFJQyxFQUFTSixFQUFpQkUsR0FBWSxDQUN6Q0csRUFBR0gsRUFDSEksR0FBRyxFQUNISCxRQUFTLElBVVYsT0FOQUksRUFBUUwsR0FBVU0sS0FBS0osRUFBT0QsUUFBU0MsRUFBUUEsRUFBT0QsUUFBU0YsR0FHL0RHLEVBQU9FLEdBQUksRUFHSkYsRUFBT0QsUUFLZkYsRUFBb0JRLEVBQUlGLEVBR3hCTixFQUFvQlMsRUFBSVYsRUFHeEJDLEVBQW9CVSxFQUFJLFNBQVNSLEVBQVNTLEVBQU1DLEdBQzNDWixFQUFvQmEsRUFBRVgsRUFBU1MsSUFDbENHLE9BQU9DLGVBQWViLEVBQVNTLEVBQU0sQ0FBRUssWUFBWSxFQUFNQyxJQUFLTCxLQUtoRVosRUFBb0JrQixFQUFJLFNBQVNoQixHQUNYLG9CQUFYaUIsUUFBMEJBLE9BQU9DLGFBQzFDTixPQUFPQyxlQUFlYixFQUFTaUIsT0FBT0MsWUFBYSxDQUFFQyxNQUFPLFdBRTdEUCxPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sS0FRdkRyQixFQUFvQnNCLEVBQUksU0FBU0QsRUFBT0UsR0FFdkMsR0FEVSxFQUFQQSxJQUFVRixFQUFRckIsRUFBb0JxQixJQUMvQixFQUFQRSxFQUFVLE9BQU9GLEVBQ3BCLEdBQVcsRUFBUEUsR0FBOEIsaUJBQVZGLEdBQXNCQSxHQUFTQSxFQUFNRyxXQUFZLE9BQU9ILEVBQ2hGLElBQUlJLEVBQUtYLE9BQU9ZLE9BQU8sTUFHdkIsR0FGQTFCLEVBQW9Ca0IsRUFBRU8sR0FDdEJYLE9BQU9DLGVBQWVVLEVBQUksVUFBVyxDQUFFVCxZQUFZLEVBQU1LLE1BQU9BLElBQ3RELEVBQVBFLEdBQTRCLGlCQUFURixFQUFtQixJQUFJLElBQUlNLEtBQU9OLEVBQU9yQixFQUFvQlUsRUFBRWUsRUFBSUUsRUFBSyxTQUFTQSxHQUFPLE9BQU9OLEVBQU1NLElBQVFDLEtBQUssS0FBTUQsSUFDOUksT0FBT0YsR0FJUnpCLEVBQW9CNkIsRUFBSSxTQUFTMUIsR0FDaEMsSUFBSVMsRUFBU1QsR0FBVUEsRUFBT3FCLFdBQzdCLFdBQXdCLE9BQU9yQixFQUFnQixTQUMvQyxXQUE4QixPQUFPQSxHQUV0QyxPQURBSCxFQUFvQlUsRUFBRUUsRUFBUSxJQUFLQSxHQUM1QkEsR0FJUlosRUFBb0JhLEVBQUksU0FBU2lCLEVBQVFDLEdBQVksT0FBT2pCLE9BQU9rQixVQUFVQyxlQUFlMUIsS0FBS3VCLEVBQVFDLElBR3pHL0IsRUFBb0JrQyxFQUFJLEdBSWpCbEMsRUFBb0JBLEVBQW9CbUMsRUFBSSxHLCtCQzdFckRyQixPQUFPQyxlQUFlYixFQUFTLGFBQWMsQ0FBRW1CLE9BQU8sSUFDdEQsSUFBSWUsT0FBaUJDLEVBQ3JCLFNBQVNDLEVBQVFYLEdBQ2IsTUFBTVksRUFBVUMsU0FBU0MsZUFBZSxnQ0FDeEMsR0FBSUYsRUFBUyxDQUNULE1BQU1HLEVBQU9ILEVBQVFJLGFBQWFoQixHQUNsQyxHQUFJZSxFQUNBLE9BQU9FLEtBQUtDLE1BQU1ILEdBRzFCLE1BQU0sSUFBSUksTUFBTSwyQkFBMkJuQixLQUUvQ3pCLEVBQVFvQyxRQUFVQSxFQVdsQnBDLEVBQVE2QyxZQVZSLFdBQ0ksR0FBSVgsRUFDQSxPQUFPQSxFQUdYLEdBREFBLEVBQWlCRSxFQUFRLGlCQUVyQixPQUFPRixFQUVYLE1BQU0sSUFBSVUsTUFBTSw2Qiw2QkNyQnBCaEMsT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBQ3RELE1BQU0yQixFQUFhLEVBQVEsR0FJM0IsU0FBU0MsRUFBVUMsR0FDZixPQUpXQyxFQUlFLEVBSkdDLEVBSUFKLEVBQVdELGNBQWNNLFVBQVksRUFKaENoQyxFQUltQzZCLEVBSGpESSxLQUFLSCxJQUFJQyxFQUFLRSxLQUFLRixJQUFJRCxFQUFLOUIsSUFEdkMsSUFBZThCLEVBQUtDLEVBQUsvQixFQU16QixNQUFNa0MsRUFBc0IsTUFDeEIsSUFBSUMsRUFDSixNQUFPLEtBQ0gsSUFBS0EsRUFBVSxDQUNYQSxFQUFXLENBQUMsQ0FBRWpCLFFBQVNDLFNBQVNpQixLQUFNUCxLQUFNLElBQzVDLElBQUssTUFBTVgsS0FBV0MsU0FBU2tCLHVCQUF1QixhQUFjLENBQ2hFLE1BQU1SLEdBQVFYLEVBQVFJLGFBQWEsYUFDL0JnQixNQUFNVCxLQUdjLFNBQXBCWCxFQUFRcUIsU0FBc0JyQixFQUFRc0IsZUFBbUQsUUFBbEN0QixFQUFRc0IsY0FBY0QsUUFHN0VKLEVBQVNNLEtBQUssQ0FBRXZCLFFBQVNBLEVBQVFzQixjQUFlWCxTQUdoRE0sRUFBU00sS0FBSyxDQUFFdkIsUUFBU0EsRUFBU1csV0FJOUMsT0FBT00sSUFwQmEsR0E2QjVCLFNBQVNPLEVBQXlCQyxHQUM5QixNQUFNQyxFQUFhWCxLQUFLWSxNQUFNRixHQUN4QkcsRUFBUVosSUFDZCxJQUFJYSxFQUFXRCxFQUFNLElBQU0sS0FDM0IsSUFBSyxNQUFNRSxLQUFTRixFQUFPLENBQ3ZCLEdBQUlFLEVBQU1uQixPQUFTZSxFQUNmLE1BQU8sQ0FBRUcsU0FBVUMsRUFBT0MsVUFBTWpDLEdBRS9CLEdBQUlnQyxFQUFNbkIsS0FBT2UsRUFDbEIsTUFBTyxDQUFFRyxXQUFVRSxLQUFNRCxHQUU3QkQsRUFBV0MsRUFFZixNQUFPLENBQUVELFlBTWIsU0FBU0csRUFBNEJDLEdBQ2pDLE1BQU1MLEVBQVFaLElBQ1JrQixFQUFXRCxFQUFTRSxPQUFPQyxRQUNqQyxJQUFJQyxHQUFNLEVBQ05DLEVBQUtWLEVBQU1XLE9BQVMsRUFDeEIsS0FBT0YsRUFBSyxFQUFJQyxHQUFJLENBQ2hCLE1BQU1FLEVBQU16QixLQUFLWSxPQUFPVSxFQUFLQyxHQUFNLEdBQzdCRyxFQUFTYixFQUFNWSxHQUFLeEMsUUFBUTBDLHdCQUM5QkQsRUFBT0UsSUFBTUYsRUFBT0csUUFBVVYsRUFDOUJJLEVBQUtFLEVBR0xILEVBQUtHLEVBR2IsTUFBTUssRUFBWWpCLEVBQU1VLEdBQ2xCUSxFQUFXRCxFQUFVN0MsUUFBUTBDLHdCQUNuQyxHQUFJSixHQUFNLEdBQUtRLEVBQVNILElBQU1ULEVBQVUsQ0FFcEMsTUFBTyxDQUFFTCxTQURTRCxFQUFNUyxHQUNNTixLQUFNYyxHQUV4QyxPQUFJUCxFQUFLLEdBQUtBLEVBQUtWLEVBQU1XLFFBQVVPLEVBQVNILElBQU1HLEVBQVNGLE9BQVNWLEVBQ3pELENBQUVMLFNBQVVnQixFQUFXZCxLQUFNSCxFQUFNVSxFQUFLLElBRTVDLENBQUVULFNBQVVnQixHQTVCdkJsRixFQUFRNkQseUJBQTJCQSxFQThCbkM3RCxFQUFRcUUsNEJBQThCQSxFQStCdENyRSxFQUFRb0YseUJBM0JSLFNBQWtDcEMsR0FDOUIsSUFBS0YsRUFBV0QsY0FBY3dDLHdCQUMxQixPQUVKLEdBQUlyQyxHQUFRLEVBRVIsWUFEQXdCLE9BQU9jLE9BQU9kLE9BQU9lLFFBQVMsR0FHbEMsTUFBTSxTQUFFckIsRUFBUSxLQUFFRSxHQUFTUCxFQUF5QmIsR0FDcEQsSUFBS2tCLEVBQ0QsT0FFSixJQUFJc0IsRUFBVyxFQUNmLE1BQU1DLEVBQU92QixFQUFTN0IsUUFBUTBDLHdCQUN4QlcsRUFBY0QsRUFBS1QsSUFDekIsR0FBSVosR0FBUUEsRUFBS3BCLE9BQVNrQixFQUFTbEIsS0FBTSxDQUlyQ3dDLEVBQVdFLEdBRmMxQyxFQUFPa0IsRUFBU2xCLE9BQVNvQixFQUFLcEIsS0FBT2tCLEVBQVNsQixPQUNqRG9CLEVBQUsvQixRQUFRMEMsd0JBQXdCQyxJQUFNVSxPQUdoRSxDQUNELE1BQU1DLEVBQW9CM0MsRUFBT0ksS0FBS1ksTUFBTWhCLEdBQzVDd0MsRUFBV0UsRUFBZUQsRUFBS1IsT0FBU1UsRUFFNUNuQixPQUFPYyxPQUFPZCxPQUFPZSxRQUFTbkMsS0FBS0YsSUFBSSxFQUFHc0IsT0FBT0MsUUFBVWUsS0FxQi9EeEYsRUFBUTRGLGlDQWxCUixTQUEwQ3RCLEdBQ3RDLE1BQU0sU0FBRUosRUFBUSxLQUFFRSxHQUFTQyxFQUE0QkMsR0FDdkQsR0FBSUosRUFBVSxDQUNWLE1BQU0yQixFQUFpQjNCLEVBQVM3QixRQUFRMEMsd0JBQ2xDZSxFQUFzQnhCLEVBQVNFLE9BQU9DLFFBQVVvQixFQUFlYixJQUNyRSxHQUFJWixFQUFNLENBQ04sTUFBTTJCLEVBQTBCRCxHQUFzQjFCLEVBQUsvQixRQUFRMEMsd0JBQXdCQyxJQUFNYSxFQUFlYixLQUVoSCxPQUFPakMsRUFETW1CLEVBQVNsQixLQUFPK0MsR0FBMkIzQixFQUFLcEIsS0FBT2tCLEVBQVNsQixPQUc1RSxDQUNELE1BQU1nRCxFQUF3QkYsRUFBc0JELEVBQXFCLE9BRXpFLE9BQU85QyxFQURNbUIsRUFBU2xCLEtBQU9nRCxJQUlyQyxPQUFPLE1BV1hoRyxFQUFRaUcsMEJBTFIsU0FBbUNDLEdBQy9CLE9BQU83QyxJQUFzQjhDLEtBQU05RCxHQUN4QkEsRUFBUUEsUUFBUStELEtBQU9GLEssNkJDMUl0Q3RGLE9BQU9DLGVBQWViLEVBQVMsYUFBYyxDQUFFbUIsT0FBTyxJQUN0RCxNQUFNa0YsRUFBcUIsRUFBUSxHQUM3QkMsRUFBVyxFQUFRLEdBQ25CQyxFQUFjLEVBQVEsR0FDdEJDLEVBQWdCLEVBQVEsR0FDeEIxRCxFQUFhLEVBQVEsR0FDckIyRCxFQUFXLEVBQVEsR0FDekIsSUFBSUMsR0FBaUIsRUFDckIsTUFBTUMsRUFBUyxJQUFJTixFQUFtQk8saUJBQ2hDQyxFQUFXL0QsRUFBV0QsY0FDdEJpRSxFQUFTQyxtQkFFZixJQUFJQyxFQUFRbEUsRUFBV1YsUUFBUSxjQUMvQjBFLEVBQU9HLFNBQVNELEdBQ2hCLE1BQU1FLEVBQVlYLEVBQVlZLHNCQUFzQkwsR0FDcER0QyxPQUFPNEMsV0FBV0MsVUFBVUgsR0FDNUIxQyxPQUFPOEMsb0JBQW9CRCxVQUFVSCxHQUNyQzFDLE9BQU8rQyxPQUFTLEtBQ1pDLEtBRUpsQixFQUFTbUIsbUJBQW1CLEtBQ3BCWixFQUFTeEIseUJBQ1RxQyxXQUFXLEtBRVAsR0FBSVYsRUFBTWQsU0FBVSxDQUNoQixNQUFNN0QsRUFBVW1FLEVBQWNQLDBCQUEwQmUsRUFBTWQsVUFDMUQ3RCxJQUNBcUUsR0FBaUIsRUFDakJGLEVBQWNwQix5QkFBeUIvQyxFQUFRVyxXQUdsRCxDQUNELE1BQU0yRSxHQUFlZCxFQUFTN0QsS0FDekJTLE1BQU1rRSxLQUNQakIsR0FBaUIsRUFDakJGLEVBQWNwQix5QkFBeUJ1QyxNQUdoRCxLQUdYLE1BQU1DLEVBQWUsTUFDakIsTUFBTUMsRUFBV3BCLEVBQVV6RCxJQUN2QjBELEdBQWlCLEVBQ2pCRixFQUFjcEIseUJBQXlCcEMsSUFDeEMsSUFDSCxNQUFPLENBQUNBLEVBQU02RCxLQUNMcEQsTUFBTVQsS0FDUDZELEVBQVM3RCxLQUFPQSxFQUNoQjZFLEVBQVM3RSxNQVJBLEdBWXJCLElBQUl3RSxFQUFtQmYsRUFBUyxLQUM1QixNQUFNcUIsRUFBWSxHQUNsQixJQUFJQyxFQUFTekYsU0FBUzBGLHFCQUFxQixPQUMzQyxHQUFJRCxFQUFRLENBQ1IsSUFBSTdILEVBQ0osSUFBS0EsRUFBSSxFQUFHQSxFQUFJNkgsRUFBT25ELE9BQVExRSxJQUFLLENBQ2hDLE1BQU0rSCxFQUFNRixFQUFPN0gsR0FDZitILEVBQUlDLFVBQVVDLFNBQVMsWUFDdkJGLEVBQUlDLFVBQVVFLE9BQU8sV0FFekJOLEVBQVVsRSxLQUFLLENBQ1h3QyxHQUFJNkIsRUFBSTdCLEdBQ1JuQixPQUFRZ0QsRUFBSWhELE9BQ1pvRCxNQUFPSixFQUFJSSxRQUduQm5CLEVBQVVvQixZQUFZLGtCQUFtQlIsS0FFOUMsSUFDSHRELE9BQU8rRCxpQkFBaUIsU0FBVSxLQUM5QjdCLEdBQWlCLEVBQ2pCYyxNQUNELEdBQ0hoRCxPQUFPK0QsaUJBQWlCLFVBQVdDLElBQy9CLEdBQUlBLEVBQU1oRyxLQUFLaUcsU0FBVzVCLEVBQVM0QixPQUduQyxPQUFRRCxFQUFNaEcsS0FBS2tHLE1BQ2YsSUFBSyxpQ0FDRC9CLEVBQU9nQywrQkFBK0JILEVBQU1oRyxLQUFLUSxNQUNqRCxNQUNKLElBQUssYUFDRDRFLEVBQWFZLEVBQU1oRyxLQUFLUSxLQUFNNkQsTUFHdkMsR0FDSHZFLFNBQVNpRyxpQkFBaUIsV0FBWUMsSUFDbEMsSUFBSzNCLEVBQVMrQiw0QkFDVixPQUdKLElBQUssSUFBSUMsRUFBT0wsRUFBTU0sT0FBUUQsRUFBTUEsRUFBT0EsRUFBS0UsV0FDNUMsR0FBcUIsTUFBakJGLEVBQUtuRixRQUNMLE9BR1IsTUFBTVksRUFBU2tFLEVBQU1RLE1BQ2ZoRyxFQUFPd0QsRUFBY1osaUNBQWlDdEIsR0FDeEMsaUJBQVR0QixHQUFzQlMsTUFBTVQsSUFDbkNrRSxFQUFVb0IsWUFBWSxXQUFZLENBQUV0RixLQUFNSSxLQUFLWSxNQUFNaEIsT0FHN0QsTUFBTWlHLEVBQXlCLENBQUMsUUFBUyxTQUFVLFVBQVcsVUFBVyxvQkFDekUzRyxTQUFTaUcsaUJBQWlCLFFBQVNDLElBQy9CLElBQUtBLEVBQ0QsT0FFSixJQUFJSyxFQUFPTCxFQUFNTSxPQUNqQixLQUFPRCxHQUFNLENBQ1QsR0FBSUEsRUFBS25GLFNBQTRCLE1BQWpCbUYsRUFBS25GLFNBQW1CbUYsRUFBS0ssS0FBTSxDQUNuRCxHQUFJTCxFQUFLcEcsYUFBYSxRQUFRMEcsV0FBVyxLQUNyQyxPQUdKLEdBQUlGLEVBQXVCRyxLQUFLQyxHQUFVUixFQUFLSyxLQUFLQyxXQUFXRSxJQUMzRCxPQUVKLE1BQU1DLEVBQVdULEVBQUtwRyxhQUFhLGNBQWdCb0csRUFBS3BHLGFBQWEsUUFFckUsTUFBSyxjQUFjOEcsS0FBS0QsUUFNeEIsR0FMSXBDLEVBQVVvQixZQUFZLFdBQVksQ0FBRVksS0FBTUksSUFDMUNkLEVBQU1nQixzQkFDTmhCLEVBQU1pQixtQkFLZFosRUFBT0EsRUFBS0UsY0FFakIsR0FDSHZFLE9BQU8rRCxpQkFBaUIsU0FBVTlCLEVBQVMsS0FDdkMsR0FBSUMsRUFDQUEsR0FBaUIsTUFFaEIsQ0FDRCxNQUFNMUQsRUFBT3dELEVBQWNaLGlDQUFpQ3BCLE9BQU9DLFNBQy9DLGlCQUFUekIsR0FBc0JTLE1BQU1ULEtBQ25Da0UsRUFBVW9CLFlBQVksYUFBYyxDQUFFdEYsU0FDdENnRSxFQUFNaEUsS0FBT0EsRUFDYjhELEVBQU9HLFNBQVNELE1BR3pCLE0sNkJDckpIcEcsT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBS3RELE1BQU1xRixFQUFnQixFQUFRLEdBd0I5QnhHLEVBQVE0RyxpQkF2QlIsTUFDSSwrQkFBK0I1RCxHQUMzQixNQUFNLFNBQUVrQixHQUFhc0MsRUFBYzNDLHlCQUF5QmIsR0FDNUQwRyxLQUFLQyxRQUFRekYsR0FBWUEsRUFBUzdCLFNBRXRDLFFBQVF1SCxHQUNKRixLQUFLRyxxQkFBcUJILEtBQUtJLFVBQy9CSixLQUFLSyxtQkFBbUJILEdBQ3hCRixLQUFLSSxTQUFXRixFQUVwQixxQkFBcUJ2SCxHQUNaQSxJQUdMQSxFQUFRMkgsVUFBWTNILEVBQVEySCxVQUFVQyxRQUFRLHdCQUF5QixLQUUzRSxtQkFBbUI1SCxHQUNWQSxJQUdMQSxFQUFRMkgsV0FBYSx3Qiw2QkN0QjdCcEosT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBU3REbkIsRUFBUXlILG1CQVJSLFNBQTRCeUMsR0FDSSxZQUF4QjVILFNBQVM2SCxZQUFvRCxrQkFBeEI3SCxTQUFTNkgsV0FDOUM3SCxTQUFTaUcsaUJBQWlCLG1CQUFvQjJCLEdBRzlDQSxNLDZCQ05SdEosT0FBT0MsZUFBZWIsRUFBUyxhQUFjLENBQUVtQixPQUFPLElBQ3RELE1BQU0yQixFQUFhLEVBQVEsR0FDM0I5QyxFQUFRbUgsc0JBQXlCTCxHQUN0QixJQUFJLE1BQ1AsWUFBWTRCLEVBQU1uRixHQUNkdUQsRUFBT3dCLFlBQVksQ0FDZkksT0FDQUQsT0FBUTNGLEVBQVdELGNBQWM0RixPQUNqQ2xGLFksaUJDYmhCLFlBVUEsSUFBSTZHLEVBQWtCLHNCQUdsQkMsRUFBTSxJQUdOQyxFQUFZLGtCQUdaQyxFQUFTLGFBR1RDLEVBQWEscUJBR2JDLEVBQWEsYUFHYkMsRUFBWSxjQUdaQyxFQUFlQyxTQUdmQyxFQUE4QixpQkFBVkMsR0FBc0JBLEdBQVVBLEVBQU9sSyxTQUFXQSxRQUFVa0ssRUFHaEZDLEVBQTBCLGlCQUFSQyxNQUFvQkEsTUFBUUEsS0FBS3BLLFNBQVdBLFFBQVVvSyxLQUd4RUMsRUFBT0osR0FBY0UsR0FBWUcsU0FBUyxjQUFUQSxHQVVqQ0MsRUFQY3ZLLE9BQU9rQixVQU9Rc0osU0FHN0JDLEVBQVlqSSxLQUFLRixJQUNqQm9JLEVBQVlsSSxLQUFLSCxJQWtCakJzSSxFQUFNLFdBQ1IsT0FBT04sRUFBS08sS0FBS0QsT0F5RG5CLFNBQVNFLEVBQVNDLEVBQU1DLEVBQU1DLEdBQzVCLElBQUlDLEVBQ0FDLEVBQ0FDLEVBQ0FDLEVBQ0FDLEVBQ0FDLEVBQ0FDLEVBQWlCLEVBQ2pCQyxHQUFVLEVBQ1ZDLEdBQVMsRUFDVEMsR0FBVyxFQUVmLEdBQW1CLG1CQUFSWixFQUNULE1BQU0sSUFBSWEsVUFBVW5DLEdBVXRCLFNBQVNvQyxFQUFXQyxHQUNsQixJQUFJQyxFQUFPYixFQUNQYyxFQUFVYixFQUtkLE9BSEFELEVBQVdDLE9BQVczSixFQUN0QmdLLEVBQWlCTSxFQUNqQlQsRUFBU04sRUFBS2tCLE1BQU1ELEVBQVNELEdBcUIvQixTQUFTRyxFQUFhSixHQUNwQixJQUFJSyxFQUFvQkwsRUFBT1AsRUFNL0IsWUFBeUIvSixJQUFqQitKLEdBQStCWSxHQUFxQm5CLEdBQ3pEbUIsRUFBb0IsR0FBT1QsR0FOSkksRUFBT04sR0FNOEJKLEVBR2pFLFNBQVNnQixJQUNQLElBQUlOLEVBQU9sQixJQUNYLEdBQUlzQixFQUFhSixHQUNmLE9BQU9PLEVBQWFQLEdBR3RCUixFQUFVdkUsV0FBV3FGLEVBekJ2QixTQUF1Qk4sR0FDckIsSUFFSVQsRUFBU0wsR0FGV2MsRUFBT1AsR0FJL0IsT0FBT0csRUFBU2YsRUFBVVUsRUFBUUQsR0FIUlUsRUFBT04sSUFHa0NILEVBb0JoQ2lCLENBQWNSLElBR25ELFNBQVNPLEVBQWFQLEdBS3BCLE9BSkFSLE9BQVU5SixFQUlObUssR0FBWVQsRUFDUFcsRUFBV0MsSUFFcEJaLEVBQVdDLE9BQVczSixFQUNmNkosR0FlVCxTQUFTa0IsSUFDUCxJQUFJVCxFQUFPbEIsSUFDUDRCLEVBQWFOLEVBQWFKLEdBTTlCLEdBSkFaLEVBQVd1QixVQUNYdEIsRUFBV3BDLEtBQ1h3QyxFQUFlTyxFQUVYVSxFQUFZLENBQ2QsUUFBZ0JoTCxJQUFaOEosRUFDRixPQXZFTixTQUFxQlEsR0FNbkIsT0FKQU4sRUFBaUJNLEVBRWpCUixFQUFVdkUsV0FBV3FGLEVBQWNwQixHQUU1QlMsRUFBVUksRUFBV0MsR0FBUVQsRUFpRXpCcUIsQ0FBWW5CLEdBRXJCLEdBQUlHLEVBR0YsT0FEQUosRUFBVXZFLFdBQVdxRixFQUFjcEIsR0FDNUJhLEVBQVdOLEdBTXRCLFlBSGdCL0osSUFBWjhKLElBQ0ZBLEVBQVV2RSxXQUFXcUYsRUFBY3BCLElBRTlCSyxFQUlULE9BeEdBTCxFQUFPMkIsRUFBUzNCLElBQVMsRUFDckI0QixFQUFTM0IsS0FDWFEsSUFBWVIsRUFBUVEsUUFFcEJMLEdBREFNLEVBQVMsWUFBYVQsR0FDSFAsRUFBVWlDLEVBQVMxQixFQUFRRyxVQUFZLEVBQUdKLEdBQVFJLEVBQ3JFTyxFQUFXLGFBQWNWLElBQVlBLEVBQVFVLFNBQVdBLEdBaUcxRFksRUFBVU0sT0FuQ1YsZ0JBQ2tCckwsSUFBWjhKLEdBQ0Z3QixhQUFheEIsR0FFZkUsRUFBaUIsRUFDakJOLEVBQVdLLEVBQWVKLEVBQVdHLE9BQVU5SixHQStCakQrSyxFQUFVUSxNQTVCVixXQUNFLFlBQW1CdkwsSUFBWjhKLEVBQXdCRCxFQUFTZ0IsRUFBYXpCLE1BNEJoRDJCLEVBMEZULFNBQVNLLEVBQVNwTSxHQUNoQixJQUFJdUgsU0FBY3ZILEVBQ2xCLFFBQVNBLElBQWtCLFVBQVJ1SCxHQUE0QixZQUFSQSxHQTRFekMsU0FBUzRFLEVBQVNuTSxHQUNoQixHQUFvQixpQkFBVEEsRUFDVCxPQUFPQSxFQUVULEdBaENGLFNBQWtCQSxHQUNoQixNQUF1QixpQkFBVEEsR0F0QmhCLFNBQXNCQSxHQUNwQixRQUFTQSxHQUF5QixpQkFBVEEsRUFzQnRCd00sQ0FBYXhNLElBQVVnSyxFQUFlOUssS0FBS2MsSUFBVW1KLEVBOEJwRHNELENBQVN6TSxHQUNYLE9BQU9rSixFQUVULEdBQUlrRCxFQUFTcE0sR0FBUSxDQUNuQixJQUFJME0sRUFBZ0MsbUJBQWpCMU0sRUFBTTJNLFFBQXdCM00sRUFBTTJNLFVBQVkzTSxFQUNuRUEsRUFBUW9NLEVBQVNNLEdBQVVBLEVBQVEsR0FBTUEsRUFFM0MsR0FBb0IsaUJBQVQxTSxFQUNULE9BQWlCLElBQVZBLEVBQWNBLEdBQVNBLEVBRWhDQSxFQUFRQSxFQUFNOEksUUFBUU0sRUFBUSxJQUM5QixJQUFJd0QsRUFBV3RELEVBQVdsQixLQUFLcEksR0FDL0IsT0FBUTRNLEdBQVlyRCxFQUFVbkIsS0FBS3BJLEdBQy9Cd0osRUFBYXhKLEVBQU02TSxNQUFNLEdBQUlELEVBQVcsRUFBSSxHQUMzQ3ZELEVBQVdqQixLQUFLcEksR0FBU2tKLEdBQU9sSixFQUd2Q2xCLEVBQU9ELFFBOUlQLFNBQWtCMEwsRUFBTUMsRUFBTUMsR0FDNUIsSUFBSVEsR0FBVSxFQUNWRSxHQUFXLEVBRWYsR0FBbUIsbUJBQVJaLEVBQ1QsTUFBTSxJQUFJYSxVQUFVbkMsR0FNdEIsT0FKSW1ELEVBQVMzQixLQUNYUSxFQUFVLFlBQWFSLElBQVlBLEVBQVFRLFFBQVVBLEVBQ3JERSxFQUFXLGFBQWNWLElBQVlBLEVBQVFVLFNBQVdBLEdBRW5EYixFQUFTQyxFQUFNQyxFQUFNLENBQzFCLFFBQVdTLEVBQ1gsUUFBV1QsRUFDWCxTQUFZVyxPLCtCQ3RUaEIsSUFBSTJCLEVBR0pBLEVBQUksV0FDSCxPQUFPdkUsS0FESixHQUlKLElBRUN1RSxFQUFJQSxHQUFLLElBQUkvQyxTQUFTLGNBQWIsR0FDUixNQUFPZ0QsR0FFYyxpQkFBWDFKLFNBQXFCeUosRUFBSXpKLFFBT3JDdkUsRUFBT0QsUUFBVWlPIiwiZmlsZSI6ImluZGV4LmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZ2V0dGVyIH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuIFx0XHR9XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBjcmVhdGUgYSBmYWtlIG5hbWVzcGFjZSBvYmplY3RcbiBcdC8vIG1vZGUgJiAxOiB2YWx1ZSBpcyBhIG1vZHVsZSBpZCwgcmVxdWlyZSBpdFxuIFx0Ly8gbW9kZSAmIDI6IG1lcmdlIGFsbCBwcm9wZXJ0aWVzIG9mIHZhbHVlIGludG8gdGhlIG5zXG4gXHQvLyBtb2RlICYgNDogcmV0dXJuIHZhbHVlIHdoZW4gYWxyZWFkeSBucyBvYmplY3RcbiBcdC8vIG1vZGUgJiA4fDE6IGJlaGF2ZSBsaWtlIHJlcXVpcmVcbiBcdF9fd2VicGFja19yZXF1aXJlX18udCA9IGZ1bmN0aW9uKHZhbHVlLCBtb2RlKSB7XG4gXHRcdGlmKG1vZGUgJiAxKSB2YWx1ZSA9IF9fd2VicGFja19yZXF1aXJlX18odmFsdWUpO1xuIFx0XHRpZihtb2RlICYgOCkgcmV0dXJuIHZhbHVlO1xuIFx0XHRpZigobW9kZSAmIDQpICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgdmFsdWUgJiYgdmFsdWUuX19lc01vZHVsZSkgcmV0dXJuIHZhbHVlO1xuIFx0XHR2YXIgbnMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIobnMpO1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkobnMsICdkZWZhdWx0JywgeyBlbnVtZXJhYmxlOiB0cnVlLCB2YWx1ZTogdmFsdWUgfSk7XG4gXHRcdGlmKG1vZGUgJiAyICYmIHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykgZm9yKHZhciBrZXkgaW4gdmFsdWUpIF9fd2VicGFja19yZXF1aXJlX18uZChucywga2V5LCBmdW5jdGlvbihrZXkpIHsgcmV0dXJuIHZhbHVlW2tleV07IH0uYmluZChudWxsLCBrZXkpKTtcbiBcdFx0cmV0dXJuIG5zO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IDIpO1xuIiwiXCJ1c2Ugc3RyaWN0XCI7XG4vKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwgeyB2YWx1ZTogdHJ1ZSB9KTtcbmxldCBjYWNoZWRTZXR0aW5ncyA9IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIGdldERhdGEoa2V5KSB7XG4gICAgY29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG4gICAgaWYgKGVsZW1lbnQpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcbiAgICAgICAgfVxuICAgIH1cbiAgICB0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuZXhwb3J0cy5nZXREYXRhID0gZ2V0RGF0YTtcbmZ1bmN0aW9uIGdldFNldHRpbmdzKCkge1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIGNhY2hlZFNldHRpbmdzID0gZ2V0RGF0YSgnZGF0YS1zZXR0aW5ncycpO1xuICAgIGlmIChjYWNoZWRTZXR0aW5ncykge1xuICAgICAgICByZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG4gICAgfVxuICAgIHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbmV4cG9ydHMuZ2V0U2V0dGluZ3MgPSBnZXRTZXR0aW5ncztcbiIsIlwidXNlIHN0cmljdFwiO1xuLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHsgdmFsdWU6IHRydWUgfSk7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5mdW5jdGlvbiBjbGFtcChtaW4sIG1heCwgdmFsdWUpIHtcbiAgICByZXR1cm4gTWF0aC5taW4obWF4LCBNYXRoLm1heChtaW4sIHZhbHVlKSk7XG59XG5mdW5jdGlvbiBjbGFtcExpbmUobGluZSkge1xuICAgIHJldHVybiBjbGFtcCgwLCBzZXR0aW5nc18xLmdldFNldHRpbmdzKCkubGluZUNvdW50IC0gMSwgbGluZSk7XG59XG5jb25zdCBnZXRDb2RlTGluZUVsZW1lbnRzID0gKCgpID0+IHtcbiAgICBsZXQgZWxlbWVudHM7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgICAgaWYgKCFlbGVtZW50cykge1xuICAgICAgICAgICAgZWxlbWVudHMgPSBbeyBlbGVtZW50OiBkb2N1bWVudC5ib2R5LCBsaW5lOiAwIH1dO1xuICAgICAgICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJyk7XG4gICAgICAgICAgICAgICAgaWYgKGlzTmFOKGxpbmUpKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoZWxlbWVudC50YWdOYW1lID09PSAnQ09ERScgJiYgZWxlbWVudC5wYXJlbnRFbGVtZW50ICYmIGVsZW1lbnQucGFyZW50RWxlbWVudC50YWdOYW1lID09PSAnUFJFJykge1xuICAgICAgICAgICAgICAgICAgICAvLyBGZW5jaGVkIGNvZGUgYmxvY2tzIGFyZSBhIHNwZWNpYWwgY2FzZSBzaW5jZSB0aGUgYGNvZGUtbGluZWAgY2FuIG9ubHkgYmUgbWFya2VkIG9uXG4gICAgICAgICAgICAgICAgICAgIC8vIHRoZSBgPGNvZGU+YCBlbGVtZW50IGFuZCBub3QgdGhlIHBhcmVudCBgPHByZT5gIGVsZW1lbnQuXG4gICAgICAgICAgICAgICAgICAgIGVsZW1lbnRzLnB1c2goeyBlbGVtZW50OiBlbGVtZW50LnBhcmVudEVsZW1lbnQsIGxpbmUgfSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBlbGVtZW50cy5wdXNoKHsgZWxlbWVudDogZWxlbWVudCwgbGluZSB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGVsZW1lbnRzO1xuICAgIH07XG59KSgpO1xuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgbWFwIHRvIGEgc3BlY2lmaWMgdGFyZ2V0IGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqXG4gKiBJZiBhbiBleGFjdCBtYXRjaCwgcmV0dXJucyBhIHNpbmdsZSBlbGVtZW50LiBJZiB0aGUgbGluZSBpcyBiZXR3ZWVuIGVsZW1lbnRzLFxuICogcmV0dXJucyB0aGUgZWxlbWVudCBwcmlvciB0byBhbmQgdGhlIGVsZW1lbnQgYWZ0ZXIgdGhlIGdpdmVuIGxpbmUuXG4gKi9cbmZ1bmN0aW9uIGdldEVsZW1lbnRzRm9yU291cmNlTGluZSh0YXJnZXRMaW5lKSB7XG4gICAgY29uc3QgbGluZU51bWJlciA9IE1hdGguZmxvb3IodGFyZ2V0TGluZSk7XG4gICAgY29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG4gICAgbGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGxpbmVzKSB7XG4gICAgICAgIGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKGVudHJ5LmxpbmUgPiBsaW5lTnVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm4geyBwcmV2aW91cywgbmV4dDogZW50cnkgfTtcbiAgICAgICAgfVxuICAgICAgICBwcmV2aW91cyA9IGVudHJ5O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91cyB9O1xufVxuZXhwb3J0cy5nZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUgPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmU7XG4vKipcbiAqIEZpbmQgdGhlIGh0bWwgZWxlbWVudHMgdGhhdCBhcmUgYXQgYSBzcGVjaWZpYyBwaXhlbCBvZmZzZXQgb24gdGhlIHBhZ2UuXG4gKi9cbmZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpIHtcbiAgICBjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcbiAgICBjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuICAgIGxldCBsbyA9IC0xO1xuICAgIGxldCBoaSA9IGxpbmVzLmxlbmd0aCAtIDE7XG4gICAgd2hpbGUgKGxvICsgMSA8IGhpKSB7XG4gICAgICAgIGNvbnN0IG1pZCA9IE1hdGguZmxvb3IoKGxvICsgaGkpIC8gMik7XG4gICAgICAgIGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgaWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG4gICAgICAgICAgICBoaSA9IG1pZDtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGxvID0gbWlkO1xuICAgICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGhpRWxlbWVudCA9IGxpbmVzW2hpXTtcbiAgICBjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG4gICAgICAgIGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGxvRWxlbWVudCwgbmV4dDogaGlFbGVtZW50IH07XG4gICAgfVxuICAgIGlmIChoaSA+IDEgJiYgaGkgPCBsaW5lcy5sZW5ndGggJiYgaGlCb3VuZHMudG9wICsgaGlCb3VuZHMuaGVpZ2h0ID4gcG9zaXRpb24pIHtcbiAgICAgICAgcmV0dXJuIHsgcHJldmlvdXM6IGhpRWxlbWVudCwgbmV4dDogbGluZXNbaGkgKyAxXSB9O1xuICAgIH1cbiAgICByZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5leHBvcnRzLmdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldCA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldDtcbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmUpIHtcbiAgICBpZiAoIXNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKS5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChsaW5lIDw9IDApIHtcbiAgICAgICAgd2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgMCk7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgeyBwcmV2aW91cywgbmV4dCB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuICAgIGlmICghcHJldmlvdXMpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBsZXQgc2Nyb2xsVG8gPSAwO1xuICAgIGNvbnN0IHJlY3QgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIGNvbnN0IHByZXZpb3VzVG9wID0gcmVjdC50b3A7XG4gICAgaWYgKG5leHQgJiYgbmV4dC5saW5lICE9PSBwcmV2aW91cy5saW5lKSB7XG4gICAgICAgIC8vIEJldHdlZW4gdHdvIGVsZW1lbnRzLiBHbyB0byBwZXJjZW50YWdlIG9mZnNldCBiZXR3ZWVuIHRoZW0uXG4gICAgICAgIGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG4gICAgICAgIGNvbnN0IGVsZW1lbnRPZmZzZXQgPSBuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNUb3A7XG4gICAgICAgIHNjcm9sbFRvID0gcHJldmlvdXNUb3AgKyBiZXR3ZWVuUHJvZ3Jlc3MgKiBlbGVtZW50T2Zmc2V0O1xuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgICAgY29uc3QgcHJvZ3Jlc3NJbkVsZW1lbnQgPSBsaW5lIC0gTWF0aC5mbG9vcihsaW5lKTtcbiAgICAgICAgc2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIChyZWN0LmhlaWdodCAqIHByb2dyZXNzSW5FbGVtZW50KTtcbiAgICB9XG4gICAgd2luZG93LnNjcm9sbCh3aW5kb3cuc2Nyb2xsWCwgTWF0aC5tYXgoMSwgd2luZG93LnNjcm9sbFkgKyBzY3JvbGxUbykpO1xufVxuZXhwb3J0cy5zY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUgPSBzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmU7XG5mdW5jdGlvbiBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldChvZmZzZXQpIHtcbiAgICBjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQob2Zmc2V0KTtcbiAgICBpZiAocHJldmlvdXMpIHtcbiAgICAgICAgY29uc3QgcHJldmlvdXNCb3VuZHMgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgICAgICBjb25zdCBvZmZzZXRGcm9tUHJldmlvdXMgPSAob2Zmc2V0IC0gd2luZG93LnNjcm9sbFkgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuICAgICAgICBpZiAobmV4dCkge1xuICAgICAgICAgICAgY29uc3QgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAobmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG4gICAgICAgICAgICBjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuICAgICAgICAgICAgcmV0dXJuIGNsYW1wTGluZShsaW5lKTtcbiAgICAgICAgfVxuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHByb2dyZXNzV2l0aGluRWxlbWVudCA9IG9mZnNldEZyb21QcmV2aW91cyAvIChwcmV2aW91c0JvdW5kcy5oZWlnaHQpO1xuICAgICAgICAgICAgY29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc1dpdGhpbkVsZW1lbnQ7XG4gICAgICAgICAgICByZXR1cm4gY2xhbXBMaW5lKGxpbmUpO1xuICAgICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xufVxuZXhwb3J0cy5nZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0O1xuLyoqXG4gKiBUcnkgdG8gZmluZCB0aGUgaHRtbCBlbGVtZW50IGJ5IHVzaW5nIGEgZnJhZ21lbnQgaWRcbiAqL1xuZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudChmcmFnbWVudCkge1xuICAgIHJldHVybiBnZXRDb2RlTGluZUVsZW1lbnRzKCkuZmluZCgoZWxlbWVudCkgPT4ge1xuICAgICAgICByZXR1cm4gZWxlbWVudC5lbGVtZW50LmlkID09PSBmcmFnbWVudDtcbiAgICB9KTtcbn1cbmV4cG9ydHMuZ2V0TGluZUVsZW1lbnRGb3JGcmFnbWVudCA9IGdldExpbmVFbGVtZW50Rm9yRnJhZ21lbnQ7XG4iLCJcInVzZSBzdHJpY3RcIjtcbi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7IHZhbHVlOiB0cnVlIH0pO1xuY29uc3QgYWN0aXZlTGluZU1hcmtlcl8xID0gcmVxdWlyZShcIi4vYWN0aXZlTGluZU1hcmtlclwiKTtcbmNvbnN0IGV2ZW50c18xID0gcmVxdWlyZShcIi4vZXZlbnRzXCIpO1xuY29uc3QgbWVzc2FnaW5nXzEgPSByZXF1aXJlKFwiLi9tZXNzYWdpbmdcIik7XG5jb25zdCBzY3JvbGxfc3luY18xID0gcmVxdWlyZShcIi4vc2Nyb2xsLXN5bmNcIik7XG5jb25zdCBzZXR0aW5nc18xID0gcmVxdWlyZShcIi4vc2V0dGluZ3NcIik7XG5jb25zdCB0aHJvdHRsZSA9IHJlcXVpcmUoXCJsb2Rhc2gudGhyb3R0bGVcIik7XG5sZXQgc2Nyb2xsRGlzYWJsZWQgPSB0cnVlO1xuY29uc3QgbWFya2VyID0gbmV3IGFjdGl2ZUxpbmVNYXJrZXJfMS5BY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IHNldHRpbmdzXzEuZ2V0U2V0dGluZ3MoKTtcbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcbi8vIFNldCBWUyBDb2RlIHN0YXRlXG5sZXQgc3RhdGUgPSBzZXR0aW5nc18xLmdldERhdGEoJ2RhdGEtc3RhdGUnKTtcbnZzY29kZS5zZXRTdGF0ZShzdGF0ZSk7XG5jb25zdCBtZXNzYWdpbmcgPSBtZXNzYWdpbmdfMS5jcmVhdGVQb3N0ZXJGb3JWc0NvZGUodnNjb2RlKTtcbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG53aW5kb3cub25sb2FkID0gKCkgPT4ge1xuICAgIHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5ldmVudHNfMS5vbmNlRG9jdW1lbnRMb2FkZWQoKCkgPT4ge1xuICAgIGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuICAgICAgICBzZXRUaW1lb3V0KCgpID0+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 5c06f2b082f3f..f581cd00252da 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; } @@ -157,7 +178,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; } diff --git a/extensions/markdown-language-features/media/pre.js b/extensions/markdown-language-features/media/pre.js index bddc3b86ac574..8268f1e2d5b60 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 1d59877196b03..0000000000000 --- 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 3f1152fc3cdd5..0000000000000 --- 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 ed302ae139840..0000000000000 --- 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 392a840c5ef6f..0000000000000 --- 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 341e509d921d1..34d6902fa4f16 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" ], @@ -25,7 +26,8 @@ "onCommand:markdown.showSource", "onCommand:markdown.showPreviewSecuritySelector", "onCommand:markdown.api.render", - "onWebviewPanel:markdown.preview" + "onWebviewPanel:markdown.preview", + "onCustomEditor:vscode.markdown.preview.editor" ], "contributes": { "commands": [ @@ -42,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", @@ -85,7 +78,7 @@ "editor/title": [ { "command": "markdown.showPreviewToSide", - "when": "editorLangId == markdown", + "when": "editorLangId == markdown && !notebookEditorFocused", "alt": "markdown.showPreview", "group": "navigation" }, @@ -121,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" }, { @@ -147,7 +140,7 @@ }, { "command": "markdown.showPreviewSecuritySelector", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.showPreviewSecuritySelector", @@ -159,7 +152,7 @@ }, { "command": "markdown.preview.refresh", - "when": "editorLangId == markdown" + "when": "editorLangId == markdown && !notebookEditorFocused" }, { "command": "markdown.preview.refresh", @@ -172,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": { @@ -209,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" }, @@ -307,6 +300,18 @@ ], "markdown.previewScripts": [ "./media/index.js" + ], + "customEditors": [ + { + "viewType": "vscode.markdown.preview.editor", + "displayName": "Markdown Preview (Experimental)", + "priority": "option", + "selector": [ + { + "filenamePattern": "*.md" + } + ] + } ] }, "scripts": { @@ -314,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" }, @@ -332,7 +339,7 @@ "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "ts-loader": "^6.2.1", - "typescript": "^3.6.4", + "typescript": "^3.7.3", "vscode": "^1.1.10", "webpack": "^4.41.2", "webpack-cli": "^3.3.0" diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index fbbab99951938..6cf645a410350 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 311c40b62d672..064c30ac96956 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -10,7 +10,7 @@ import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine, getLineElem import { getSettings, getData } from './settings'; import throttle = require('lodash.throttle'); -declare var acquireVsCodeApi: any; +declare let acquireVsCodeApi: any; let scrollDisabled = true; const marker = new ActiveLineMarker(); @@ -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/scroll-sync.ts b/extensions/markdown-language-features/preview-src/scroll-sync.ts index 2d12fd2731a13..e833862066ed8 100644 --- a/extensions/markdown-language-features/preview-src/scroll-sync.ts +++ b/extensions/markdown-language-features/preview-src/scroll-sync.ts @@ -5,6 +5,7 @@ import { getSettings } from './settings'; +const codeLineClass = 'code-line'; function clamp(min: number, max: number, value: number) { return Math.min(max, Math.max(min, value)); @@ -25,7 +26,7 @@ const getCodeLineElements = (() => { return () => { if (!elements) { elements = [{ element: document.body, line: 0 }]; - for (const element of document.getElementsByClassName('code-line')) { + for (const element of document.getElementsByClassName(codeLineClass)) { const line = +element.getAttribute('data-line')!; if (isNaN(line)) { continue; @@ -75,7 +76,7 @@ export function getLineElementsAtPageOffset(offset: number): { previous: CodeLin let hi = lines.length - 1; while (lo + 1 < hi) { const mid = Math.floor((lo + hi) / 2); - const bounds = lines[mid].element.getBoundingClientRect(); + const bounds = getElementBounds(lines[mid]); if (bounds.top + bounds.height >= position) { hi = mid; } @@ -84,7 +85,7 @@ export function getLineElementsAtPageOffset(offset: number): { previous: CodeLin } } const hiElement = lines[hi]; - const hiBounds = hiElement.element.getBoundingClientRect(); + const hiBounds = getElementBounds(hiElement); if (hi >= 1 && hiBounds.top > position) { const loElement = lines[lo]; return { previous: loElement, next: hiElement }; @@ -95,6 +96,24 @@ export function getLineElementsAtPageOffset(offset: number): { previous: CodeLin return { previous: hiElement }; } +function getElementBounds({ element }: CodeLineElement): { top: number, height: number } { + const myBounds = element.getBoundingClientRect(); + + // Some code line elements may contain other code line elements. + // In those cases, only take the height up to that child. + const codeLineChild = element.querySelector(`.${codeLineClass}`); + if (codeLineChild) { + const childBounds = codeLineChild.getBoundingClientRect(); + const height = Math.max(1, (childBounds.top - myBounds.top)); + return { + top: myBounds.top, + height: height + }; + } + + return myBounds; +} + /** * Attempt to reveal the element for a source line in the editor. */ @@ -113,7 +132,7 @@ export function scrollToRevealSourceLine(line: number) { return; } let scrollTo = 0; - const rect = previous.element.getBoundingClientRect(); + const rect = getElementBounds(previous); const previousTop = rect.top; if (next && next.line !== previous.line) { // Between two elements. Go to percentage offset between them. @@ -130,10 +149,10 @@ export function scrollToRevealSourceLine(line: number) { export function getEditorLineNumberForPageOffset(offset: number) { const { previous, next } = getLineElementsAtPageOffset(offset); if (previous) { - const previousBounds = previous.element.getBoundingClientRect(); + const previousBounds = getElementBounds(previous); const offsetFromPrevious = (offset - window.scrollY - previousBounds.top); if (next) { - const progressBetweenElements = offsetFromPrevious / (next.element.getBoundingClientRect().top - previousBounds.top); + const progressBetweenElements = offsetFromPrevious / (getElementBounds(next).top - previousBounds.top); const line = previous.line + progressBetweenElements * (next.line - previous.line); return clampLine(line); } else { diff --git a/extensions/markdown-language-features/preview-src/settings.ts b/extensions/markdown-language-features/preview-src/settings.ts index c77081b265989..470246f94c1d6 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 85159a000d958..e19cd4a675d7d 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/schemas/package.schema.json b/extensions/markdown-language-features/schemas/package.schema.json index e76ce046c27f7..5591d0b0032c0 100644 --- a/extensions/markdown-language-features/schemas/package.schema.json +++ b/extensions/markdown-language-features/schemas/package.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "title": "Markdown contributions to package.json", "type": "object", "properties": { @@ -29,4 +29,4 @@ } } } -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/src/commands/openDocumentLink.ts b/extensions/markdown-language-features/src/commands/openDocumentLink.ts index ef173b60854eb..c17dc17cb7616 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); + public async execute(args: OpenDocumentLinkArgs) { + return OpenDocumentLinkCommand.execute(this.engine, args); + } + + 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); - return this.tryOpen(targetPath, args, column).catch(() => { - if (targetPath && extname(targetPath) === '') { - return this.tryOpen(targetPath + '.md', args, column); + 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); } - const targetResource = vscode.Uri.file(targetPath); - return Promise.resolve(undefined) - .then(() => vscode.commands.executeCommand('vscode.open', targetResource, column)) - .then(() => undefined); - }); + await vscode.commands.executeCommand('vscode.open', targetResource, column); + return undefined; + } } - private async tryOpen(path: string, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { - const resource = vscode.Uri.file(path); + private static async tryOpen(engine: MarkdownEngine, resource: vscode.Uri, args: OpenDocumentLinkArgs, column: vscode.ViewColumn) { 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); + 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/commands/showPreview.ts b/extensions/markdown-language-features/src/commands/showPreview.ts index bd59d575bbbd3..fde3bd7192d82 100644 --- a/extensions/markdown-language-features/src/commands/showPreview.ts +++ b/extensions/markdown-language-features/src/commands/showPreview.ts @@ -6,9 +6,8 @@ import * as vscode from 'vscode'; import { Command } from '../commandManager'; -import { MarkdownPreviewManager } from '../features/previewManager'; +import { MarkdownPreviewManager, DynamicPreviewSettings } from '../features/previewManager'; import { TelemetryReporter } from '../telemetryReporter'; -import { PreviewSettings } from '../features/preview'; interface ShowPreviewSettings { readonly sideBySide?: boolean; @@ -39,7 +38,7 @@ async function showPreview( } const resourceColumn = (vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) || vscode.ViewColumn.One; - webviewManager.preview(resource, { + webviewManager.openDynamicPreview(resource, { resourceColumn: resourceColumn, previewColumn: previewSettings.sideBySide ? resourceColumn + 1 : resourceColumn, locked: !!previewSettings.locked @@ -59,7 +58,7 @@ export class ShowPreviewCommand implements Command { private readonly telemetryReporter: TelemetryReporter ) { } - public execute(mainUri?: vscode.Uri, allUris?: vscode.Uri[], previewSettings?: PreviewSettings) { + public execute(mainUri?: vscode.Uri, allUris?: vscode.Uri[], previewSettings?: DynamicPreviewSettings) { for (const uri of Array.isArray(allUris) ? allUris : [mainUri]) { showPreview(this.webviewManager, this.telemetryReporter, uri, { sideBySide: false, @@ -77,7 +76,7 @@ export class ShowPreviewToSideCommand implements Command { private readonly telemetryReporter: TelemetryReporter ) { } - public execute(uri?: vscode.Uri, previewSettings?: PreviewSettings) { + public execute(uri?: vscode.Uri, previewSettings?: DynamicPreviewSettings) { showPreview(this.webviewManager, this.telemetryReporter, uri, { sideBySide: true, locked: previewSettings && previewSettings.locked diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 7180f0a69c84a..f5c0b4114a4d8 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -15,9 +15,9 @@ 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 +33,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)); @@ -49,14 +49,13 @@ function registerMarkdownLanguageFeatures( symbolProvider: MDDocumentSymbolProvider, engine: MarkdownEngine ): vscode.Disposable { - const selector: vscode.DocumentSelector = [ - { language: 'markdown', scheme: 'file' }, - { language: 'markdown', scheme: 'untitled' } - ]; + const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' }; + + const charPattern = '(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})'; return vscode.Disposable.from( vscode.languages.setLanguageConfiguration('markdown', { - wordPattern: new RegExp('(\\p{Alphabetic}|\\p{Number}|\\p{Nonspacing_Mark})+', 'ug'), + wordPattern: new RegExp(`${charPattern}((${charPattern}|[_])?${charPattern})*`, 'ug'), }), vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider), vscode.languages.registerDocumentLinkProvider(selector, new LinkProvider()), diff --git a/extensions/markdown-language-features/src/features/documentLinkProvider.ts b/extensions/markdown-language-features/src/features/documentLinkProvider.ts index 441055e5e71ef..89237df66d645 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; @@ -91,27 +111,25 @@ export default class LinkProvider implements vscode.DocumentLinkProvider { 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 4850b3759ded1..590a7a1b24652 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -77,6 +77,9 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi return token.map[1] > token.map[0]; case 'html_block': + if (isRegionMarker(token)) { + return false; + } return token.map[1] > token.map[0] + 1; default: @@ -92,7 +95,7 @@ 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, listItem.type === 'html_block' && listItem.content.startsWith('<!--') ? vscode.FoldingRangeKind.Comment : undefined); }); } } diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 16636dd158524..f2fb800347ce7 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 * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import { OpenDocumentLinkCommand, resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; import { Logger } from '../logger'; -import { MarkdownContentProvider } from './previewContentProvider'; +import { MarkdownContributionProvider } from '../markdownExtensions'; import { Disposable } from '../util/dispose'; - -import * as nls from 'vscode-nls'; -import { getVisibleLine, MarkdownFileTopmostLineMonitor } 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 { normalizeResource, WebviewResourceProvider } from '../util/resources'; +import { getVisibleLine, TopmostLineMonitor } from '../util/topmostLineMonitor'; +import { MarkdownPreviewConfigurationManager } from './previewConfig'; +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,125 +76,94 @@ export class PreviewDocumentVersion { } } -export class MarkdownPreview extends Disposable { +interface MarkdownPreviewDelegate { + getTitle?(resource: vscode.Uri): string; + getAdditionalState(): {}, + openPreviewLinkToMarkdownFile(markdownLink: vscode.Uri, fragment: string): void; +} - public static readonly viewType = 'markdown.preview'; +class StartingScrollLine { + public readonly type = 'line'; - private _resource: vscode.Uri; - private _locked: boolean; + 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 readonly _resource: vscode.Uri; + private readonly _webviewPanel: vscode.WebviewPanel; - 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 forceUpdate = false; 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 async revive( + constructor( webview: vscode.WebviewPanel, - state: any, - contentProvider: MarkdownContentProvider, - previewConfigurations: MarkdownPreviewConfigurationManager, - logger: Logger, - topmostLineMonitor: MarkdownFileTopmostLineMonitor, - contributionProvider: MarkdownContributionProvider, - ): Promise<MarkdownPreview> { - const resource = vscode.Uri.parse(state.resource); - const locked = state.locked; - const line = state.line; - const resourceColumn = state.resourceColumn; - - const preview = new MarkdownPreview( - webview, - resource, - locked, - resourceColumn, - contentProvider, - previewConfigurations, - logger, - topmostLineMonitor, - contributionProvider); - - preview.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, contributionProvider.contributions); - - if (!isNaN(line)) { - preview.line = line; - } - await preview.doUpdate(); - return preview; - } - - public static create( resource: vscode.Uri, - previewColumn: vscode.ViewColumn, - resourceColumn: vscode.ViewColumn, - locked: boolean, - contentProvider: MarkdownContentProvider, - previewConfigurations: MarkdownPreviewConfigurationManager, - logger: Logger, - topmostLineMonitor: MarkdownFileTopmostLineMonitor, - contributionProvider: MarkdownContributionProvider - ): MarkdownPreview { - const webview = vscode.window.createWebviewPanel( - MarkdownPreview.viewType, - MarkdownPreview.getPreviewTitle(resource, locked), - previewColumn, { - enableFindWidget: true, - ...MarkdownPreview.getWebviewOptions(resource, contributionProvider.contributions) - }); - - return new MarkdownPreview( - webview, - resource, - locked, - resourceColumn, - contentProvider, - previewConfigurations, - logger, - topmostLineMonitor, - contributionProvider); - } - - private constructor( - webview: vscode.WebviewPanel, - resource: vscode.Uri, - locked: boolean, - private readonly _resourceColumn: vscode.ViewColumn, + startingScroll: StartingScrollLocation | undefined, + private readonly delegate: MarkdownPreviewDelegate, + private readonly engine: MarkdownEngine, private readonly _contentProvider: MarkdownContentProvider, private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, private readonly _logger: Logger, - topmostLineMonitor: MarkdownFileTopmostLineMonitor, private readonly _contributionProvider: MarkdownContributionProvider, ) { super(); + + this._webviewPanel = webview; this._resource = resource; - this._locked = locked; - this.editor = webview; - this._register(this.editor.onDidDispose(() => { - this.dispose(); - })); + switch (startingScroll?.type) { + case 'line': + if (!isNaN(startingScroll.line!)) { + this.line = startingScroll.line; + } + break; - this._register(this.editor.onDidChangeViewState(e => { - this._onDidChangeViewStateEmitter.fire(e); - })); + case 'fragment': + this.scrollToFragment = startingScroll.fragment; + break; + } 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; } switch (e.type) { case 'cacheImageSizes': - this.onCacheImageSizes(e.body); + this.imageInfo = e.body; break; case 'revealLine': @@ -210,179 +183,70 @@ export class MarkdownPreview 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.onDidChangeTopmostLine(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); - } - })); + 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._onDidChangeViewStateEmitter.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(); + if (this.firstUpdate) { + this.updatePreview(true); } else { - this.throttleTimer = setTimeout(() => this.doUpdate(), 300); + this.throttleTimer = setTimeout(() => this.updatePreview(true), this.delay); } } this.firstUpdate = false; } - public refresh() { - this.forceUpdate = true; - 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: MarkdownPreview): 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 = MarkdownPreview.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; } @@ -391,36 +255,26 @@ export class MarkdownPreview 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(): 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; @@ -430,57 +284,22 @@ export class MarkdownPreview extends Disposable { return; } - const pendingVersion = new PreviewDocumentVersion(markdownResource, document.version); - if (!this.forceUpdate && this.currentVersion && this.currentVersion.equals(pendingVersion)) { + 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.forceUpdate = false; 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); - private static getWebviewOptions( - resource: vscode.Uri, - contributions: MarkdownContributions - ): vscode.WebviewOptions { - return { - enableScripts: true, - localResourceRoots: MarkdownPreview.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))); + // 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); } - - return baseRoots.map(root => normalizeResource(base, root)); } private onDidScrollPreview(line: number) { @@ -508,6 +327,9 @@ export class MarkdownPreview extends Disposable { } private async onDidClickPreview(line: number): Promise<void> { + // fix #82457, find currently opened but unfocused source tab + await vscode.commands.executeCommand('markdown.showSource'); + for (const visibleEditor of vscode.window.visibleTextEditors) { if (this.isPreviewOf(visibleEditor.document.uri)) { const editor = await vscode.window.showTextDocument(visibleEditor.document, visibleEditor.viewColumn); @@ -525,23 +347,55 @@ export class MarkdownPreview 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 = MarkdownPreview.getPreviewTitle(this._resource, this._locked); - this.editor.iconPath = this.iconPath; - this.editor.webview.options = MarkdownPreview.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); @@ -549,24 +403,343 @@ export class MarkdownPreview 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() }); } - private async onCacheImageSizes(imageInfo: { id: string, width: number, height: number; }[]) { - this.imageInfo = imageInfo; + //#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 PreviewSettings { +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 previewColumn: 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/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 0be66d1d63043..a8a64e5270da6 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 3fc8106908b5c..3c8ddd7744672 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -5,77 +5,125 @@ import * as vscode from 'vscode'; import { Logger } from '../logger'; +import { MarkdownEngine } from '../markdownEngine'; import { MarkdownContributionProvider } from '../markdownExtensions'; -import { disposeAll, Disposable } from '../util/dispose'; -import { MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor'; -import { MarkdownPreview, PreviewSettings } from './preview'; +import { Disposable, disposeAll } from '../util/dispose'; +import { TopmostLineMonitor } from '../util/topmostLineMonitor'; +import { DynamicMarkdownPreview, ManagedMarkdownPreview, StaticMarkdownPreview } from './preview'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; import { MarkdownContentProvider } from './previewContentProvider'; +export interface DynamicPreviewSettings { + readonly resourceColumn: vscode.ViewColumn; + readonly previewColumn: vscode.ViewColumn; + readonly locked: boolean; +} + +class PreviewStore<T extends ManagedMarkdownPreview> extends Disposable { + + private readonly _previews = new Set<T>(); + + public dispose(): void { + super.dispose(); + for (const preview of this._previews) { + preview.dispose(); + } + this._previews.clear(); + } -export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer { + [Symbol.iterator](): Iterator<T> { + return this._previews[Symbol.iterator](); + } + + 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; + } + } + return undefined; + } + + public add(preview: T) { + this._previews.add(preview); + } + + public delete(preview: T) { + this._previews.delete(preview); + } +} + +export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.CustomTextEditorProvider { private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus'; - private readonly _topmostLineMonitor = new MarkdownFileTopmostLineMonitor(); + private readonly _topmostLineMonitor = new TopmostLineMonitor(); private readonly _previewConfigurations = new MarkdownPreviewConfigurationManager(); - private readonly _previews: MarkdownPreview[] = []; - private _activePreview: MarkdownPreview | undefined = undefined; + + private readonly _dynamicPreviews = this._register(new PreviewStore<DynamicMarkdownPreview>()); + private readonly _staticPreviews = this._register(new PreviewStore<StaticMarkdownPreview>()); + + 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(MarkdownPreview.viewType, this)); - } - - public dispose(): void { - super.dispose(); - disposeAll(this._previews); + this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this)); + this._register(vscode.window.registerCustomEditorProvider(this.customEditorViewType, this)); } public refresh() { - for (const preview of this._previews) { + for (const preview of this._dynamicPreviews) { + preview.refresh(); + } + for (const preview of this._staticPreviews) { preview.refresh(); } } public updateConfiguration() { - for (const preview of this._previews) { + for (const preview of this._dynamicPreviews) { + preview.updateConfiguration(); + } + for (const preview of this._staticPreviews) { preview.updateConfiguration(); } } - public preview( + public openDynamicPreview( resource: vscode.Uri, - previewSettings: PreviewSettings + settings: DynamicPreviewSettings ): void { - let preview = this.getExistingPreview(resource, previewSettings); + let preview = this._dynamicPreviews.get(resource, settings); if (preview) { - preview.reveal(previewSettings.previewColumn); + preview.reveal(settings.previewColumn); } else { - preview = this.createNewPreview(resource, previewSettings); + preview = this.createNewDynamicPreview(resource, settings); } preview.update(resource); } public get activePreviewResource() { - return this._activePreview && this._activePreview.resource; + return this._activePreview?.resource; } public get activePreviewResourceColumn() { - return this._activePreview && this._activePreview.resourceColumn; + return this._activePreview?.resourceColumn; } 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 - for (const otherPreview of this._previews) { + for (const otherPreview of this._dynamicPreviews) { if (otherPreview !== preview && preview.matches(otherPreview)) { otherPreview.dispose(); } @@ -87,71 +135,101 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview webview: vscode.WebviewPanel, state: any ): Promise<void> { - const preview = await MarkdownPreview.revive( + const resource = vscode.Uri.parse(state.resource); + const locked = state.locked; + const line = state.line; + const resourceColumn = state.resourceColumn; + + const preview = await DynamicMarkdownPreview.revive( + { resource, locked, line, resourceColumn }, webview, - state, this._contentProvider, this._previewConfigurations, this._logger, this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); - this.registerPreview(preview); + this.registerDynamicPreview(preview); } - private getExistingPreview( - resource: vscode.Uri, - previewSettings: PreviewSettings - ): MarkdownPreview | undefined { - return this._previews.find(preview => - preview.matchesResource(resource, previewSettings.previewColumn, previewSettings.locked)); + public async resolveCustomTextEditor( + document: vscode.TextDocument, + webview: vscode.WebviewPanel + ): Promise<void> { + const preview = StaticMarkdownPreview.revive( + document.uri, + webview, + this._contentProvider, + this._previewConfigurations, + this._logger, + this._contributions, + this._engine); + this.registerStaticPreview(preview); } - private createNewPreview( + private createNewDynamicPreview( resource: vscode.Uri, - previewSettings: PreviewSettings - ): MarkdownPreview { - const preview = MarkdownPreview.create( - resource, + previewSettings: DynamicPreviewSettings + ): DynamicMarkdownPreview { + const preview = DynamicMarkdownPreview.create( + { + resource, + resourceColumn: previewSettings.resourceColumn, + locked: previewSettings.locked, + }, previewSettings.previewColumn, - previewSettings.resourceColumn, - previewSettings.locked, this._contentProvider, this._previewConfigurations, this._logger, this._topmostLineMonitor, - this._contributions); + this._contributions, + this._engine); this.setPreviewActiveContext(true); this._activePreview = preview; - return this.registerPreview(preview); + return this.registerDynamicPreview(preview); } - private registerPreview( - preview: MarkdownPreview - ): MarkdownPreview { - this._previews.push(preview); + private registerDynamicPreview(preview: DynamicMarkdownPreview): DynamicMarkdownPreview { + this._dynamicPreviews.add(preview); preview.onDispose(() => { - const existing = this._previews.indexOf(preview); - if (existing === -1) { - return; - } + this._dynamicPreviews.delete(preview); + }); - this._previews.splice(existing, 1); - if (this._activePreview === preview) { - this.setPreviewActiveContext(false); - this._activePreview = undefined; - } + this.trackActive(preview); + + preview.onDidChangeViewState(() => { + // Remove other dynamic previews in our column + disposeAll(Array.from(this._dynamicPreviews).filter(otherPreview => preview !== otherPreview && preview.matches(otherPreview))); }); + return preview; + } + + private registerStaticPreview(preview: StaticMarkdownPreview): StaticMarkdownPreview { + this._staticPreviews.add(preview); + + preview.onDispose(() => { + this._staticPreviews.delete(preview); + }); + + this.trackActive(preview); + return preview; + } + private trackActive(preview: ManagedMarkdownPreview): void { preview.onDidChangeViewState(({ webviewPanel }) => { - disposeAll(this._previews.filter(otherPreview => preview !== otherPreview && preview!.matches(otherPreview))); this.setPreviewActiveContext(webviewPanel.active); this._activePreview = webviewPanel.active ? preview : undefined; }); - return preview; + preview.onDispose(() => { + if (this._activePreview === preview) { + this.setPreviewActiveContext(false); + this._activePreview = undefined; + } + }); } private setPreviewActiveContext(value: boolean) { diff --git a/extensions/markdown-language-features/src/logger.ts b/extensions/markdown-language-features/src/logger.ts index 4ee89cbb811a9..2136a0530840a 100644 --- a/extensions/markdown-language-features/src/logger.ts +++ b/extensions/markdown-language-features/src/logger.ts @@ -41,13 +41,21 @@ export class Logger { public log(message: string, data?: any): void { if (this.trace === Trace.Verbose) { - this.appendLine(`[Log - ${(new Date().toLocaleTimeString())}] ${message}`); + this.appendLine(`[Log - ${this.now()}] ${message}`); if (data) { this.appendLine(Logger.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(); + } + public updateConfiguration() { this.trace = this.readTrace(); } @@ -73,3 +81,7 @@ export class Logger { return JSON.stringify(data, undefined, 2); } } + +function padLeft(s: string, n: number, pad = ' ') { + return pad.repeat(Math.max(0, n - s.length)) + s; +} diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index d3b88f06c42e7..8bb509d456577 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,13 +241,18 @@ export class MarkdownEngine { if (uri.path[0] === '/') { const root = vscode.workspace.getWorkspaceFolder(this.currentDocument!); if (root) { - uri = uri.with({ - path: path.join(root.uri.fsPath, uri.path), + const fileUri = vscode.Uri.joinPath(root.uri, uri.fsPath); + uri = fileUri.with({ + scheme: uri.scheme, + fragment: uri.fragment, + query: uri.query, }); } } - 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 }); @@ -352,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 b03c5df80bd48..387a41388c06d 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/telemetryReporter.ts b/extensions/markdown-language-features/src/telemetryReporter.ts index d00dca386d1a7..1104332512de3 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 0000000000000..9b26d995f27a5 --- /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 ba6cfa2567fd5..7edbec869c87f 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,24 +118,24 @@ 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)); } }); }); diff --git a/extensions/markdown-language-features/src/test/engine.ts b/extensions/markdown-language-features/src/test/engine.ts index cb74a5b695f22..e75be4abac51a 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 2ad8444d96708..11a4c60752d24 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 c2472e5a4ec99..052216f90f5e5 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 bd7e03c9a5209..0eb9bc92487a4 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: true, + 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/util.ts b/extensions/markdown-language-features/src/test/util.ts new file mode 100644 index 0000000000000..d50e9ca5db27d --- /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 0000000000000..b009808968d73 --- /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 74383466f729e..3545d03a54d40 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/src/util/topmostLineMonitor.ts b/extensions/markdown-language-features/src/util/topmostLineMonitor.ts index 4348e142f59b9..57395fbc691cc 100644 --- a/extensions/markdown-language-features/src/util/topmostLineMonitor.ts +++ b/extensions/markdown-language-features/src/util/topmostLineMonitor.ts @@ -4,33 +4,28 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { disposeAll } from '../util/dispose'; +import { Disposable } from '../util/dispose'; import { isMarkdownFile } from './file'; -export class MarkdownFileTopmostLineMonitor { - private readonly disposables: vscode.Disposable[] = []; +export class TopmostLineMonitor extends Disposable { private readonly pendingUpdates = new Map<string, number>(); - private readonly throttle = 50; constructor() { - vscode.window.onDidChangeTextEditorVisibleRanges(event => { + super(); + this._register(vscode.window.onDidChangeTextEditorVisibleRanges(event => { if (isMarkdownFile(event.textEditor.document)) { const line = getVisibleLine(event.textEditor); if (typeof line === 'number') { this.updateLine(event.textEditor.document.uri, line); } } - }, null, this.disposables); - } - - dispose() { - disposeAll(this.disposables); + })); } - private readonly _onDidChangeTopmostLineEmitter = new vscode.EventEmitter<{ resource: vscode.Uri, line: number }>(); - public readonly onDidChangeTopmostLine = this._onDidChangeTopmostLineEmitter.event; + private readonly _onChanged = this._register(new vscode.EventEmitter<{ readonly resource: vscode.Uri, readonly line: number }>()); + public readonly onDidChanged = this._onChanged.event; private updateLine( resource: vscode.Uri, @@ -41,7 +36,7 @@ export class MarkdownFileTopmostLineMonitor { // schedule update setTimeout(() => { if (this.pendingUpdates.has(key)) { - this._onDidChangeTopmostLineEmitter.fire({ + this._onChanged.fire({ resource, line: this.pendingUpdates.get(key) as number }); 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 0000000000000..9d70918067bfe --- /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 0000000000000..730f53ee6ce22 --- /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 0000000000000..f5c9bec43acab --- /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 58996c3eb7d7a..865444125fe91 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 cf99e4dacff35..5f1895e968d5f 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" @@ -4528,10 +4533,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.6.4: - version "3.6.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d" - integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg== +typescript@^3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69" + integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw== uc.micro@^1.0.1: version "1.0.3" @@ -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 36e8b0714faf4..f071cfb7c71b2 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 0000000000000..e4171bed927ea --- /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 45600607fc5b4..7a04ca98e973b 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 815a412492a71..d438b96ca6131 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 3310dac7e21cc..3353f40b31649 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 f79c6650af018..ca45b9a16f8da 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/documentMergeConflict.ts b/extensions/merge-conflict/src/documentMergeConflict.ts index 560c7ed8c7282..d3d421f22d12a 100644 --- a/extensions/merge-conflict/src/documentMergeConflict.ts +++ b/extensions/merge-conflict/src/documentMergeConflict.ts @@ -35,7 +35,7 @@ export class DocumentMergeConflict implements interfaces.IDocumentMergeConflict public applyEdit(type: interfaces.CommitType, document: vscode.TextDocument, edit: { replace(range: vscode.Range, newText: string): void; }): void { // Each conflict is a set of ranges as follows, note placements or newlines - // which may not in in spans + // which may not in spans // [ Conflict Range -- (Entire content below) // [ Current Header ]\n -- >>>>> Header // [ Current Content ] -- (content) @@ -75,4 +75,4 @@ export class DocumentMergeConflict implements interfaces.IDocumentMergeConflict private isNewlineOnly(text: string) { return text === '\n' || text === '\r\n'; } -} \ No newline at end of file +} diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index 41be7a803e220..74a7d9dfe997f 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/microsoft-authentication/.vscodeignore b/extensions/microsoft-authentication/.vscodeignore new file mode 100644 index 0000000000000..46f23a20dbabb --- /dev/null +++ b/extensions/microsoft-authentication/.vscodeignore @@ -0,0 +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 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 0000000000000..d2f4d9ee94bdf --- /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 0000000000000..e18229a8e170a --- /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 0000000000000..45c42c75ad5c4 --- /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/microsoft-authentication/media/auth.html b/extensions/microsoft-authentication/media/auth.html new file mode 100644 index 0000000000000..0fcba4e3c625b --- /dev/null +++ b/extensions/microsoft-authentication/media/auth.html @@ -0,0 +1,38 @@ +<!-- Copyright (C) Microsoft Corporation. All rights reserved. --> +<!DOCTYPE html> +<html> + +<head> + <meta charset="utf-8" /> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <title>Azure Account - Sign In + + + + + + + Visual Studio Code + +
+
+ You are signed in now and can close this page. +
+
+ An error occurred while signing in: +
+
+
+ + + + diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json new file mode 100644 index 0000000000000..64becb5046889 --- /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 0000000000000..c0bb4c4a6a0e9 --- /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 0000000000000..6f437ca401a48 --- /dev/null +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -0,0 +1,618 @@ +/*--------------------------------------------------------------------------------------------- + * 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 from 'node-fetch'; +import { sha256 } from './env/node/sha256'; + +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; +} + +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(); + +export const REFRESH_NETWORK_FAILURE = 'Network failure'; + +class UriEventHandler extends vscode.EventEmitter implements vscode.UriHandler { + public handleUri(uri: vscode.Uri) { + this.fire(uri); + } +} + +export class AzureActiveDirectoryService { + private _tokens: IToken[] = []; + private _refreshTimeouts: Map = new Map(); + private _uriHandler: UriEventHandler; + + constructor() { + this._uriHandler = new UriEventHandler(); + vscode.window.registerUriHandler(this._uriHandler); + } + + public async initialize(): Promise { + const storedData = await keychain.getToken(); + 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.pollForChange(); + } + + private parseStoredData(data: string): IStoredSession[] { + return JSON.parse(data); + } + + private async storeTokenData(): Promise { + 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 pollForChange() { + setTimeout(async () => { + 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: [] }); + } + + this.pollForChange(); + }, 1000 * 30); + } + + private async convertToSession(token: IToken): Promise { + 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 { + 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 { + return Promise.all(this._tokens.map(token => this.convertToSession(token))); + } + + public async login(scope: string): Promise { + 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); + } + }); + } + + 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 ''; + } + } + + private async loginWithoutLocalServer(scope: string): Promise { + 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); + }); + + return Promise.race([this.handleCodeResponse(state, codeVerifier, scope), timeoutPromise]); + } + + private async handleCodeResponse(state: string, codeVerifier: string, scope: string): Promise { + 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; + + // Workaround double encoding issues of state in web + if (query.state !== state && decodeURIComponent(query.state) !== state) { + throw new Error('State does not match.'); + } + + const token = await this.exchangeCodeForToken(code, codeVerifier, 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 { + 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 { + const claims = this.getTokenClaims(json.access_token); + 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 { + 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 { + try { + Logger.info('Refreshing token...'); + const postData = querystring.stringify({ + refresh_token: refreshToken, + client_id: clientId, + grant_type: 'refresh_token', + scope: scope + }); + + const 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 + }); + + 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 { + Logger.error('Refreshing token failed'); + throw new Error('Refreshing token failed.'); + } + } catch (e) { + Logger.error('Refreshing token failed'); + throw new Error(REFRESH_NETWORK_FAILURE); + } + } + + 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 { + 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/microsoft-authentication/src/authServer.ts b/extensions/microsoft-authentication/src/authServer.ts new file mode 100644 index 0000000000000..caae72ba52c88 --- /dev/null +++ b/extensions/microsoft-authentication/src/authServer.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as http from 'http'; +import * as url from 'url'; +import * as fs from 'fs'; +import * as path from 'path'; + +interface Deferred { + resolve: (result: T | Promise) => void; + reject: (reason: any) => void; +} + +/** + * Asserts that the argument passed in is neither undefined nor null. + */ +function assertIsDefined(arg: T | null | undefined): T { + if (typeof (arg) === 'undefined' || arg === null) { + throw new Error('Assertion Failed: argument is undefined or null'); + } + + return arg; +} + +export async function startServer(server: http.Server): Promise { + let portTimer: NodeJS.Timer; + + function cancelPortTimer() { + clearTimeout(portTimer); + } + + const port = new Promise((resolve, reject) => { + portTimer = setTimeout(() => { + reject(new Error('Timeout waiting for port')); + }, 5000); + + server.on('listening', () => { + const address = server.address(); + if (typeof address === 'string') { + resolve(address); + } else { + resolve(assertIsDefined(address).port.toString()); + } + }); + + server.on('error', _ => { + reject(new Error('Error listening to server')); + }); + + server.on('close', () => { + reject(new Error('Closed')); + }); + + server.listen(0); + }); + + port.then(cancelPortTimer, cancelPortTimer); + return port; +} + +function sendFile(res: http.ServerResponse, filepath: string, contentType: string) { + fs.readFile(filepath, (err, body) => { + if (err) { + console.error(err); + res.writeHead(404); + res.end(); + } else { + res.writeHead(200, { + 'Content-Length': body.length, + 'Content-Type': contentType + }); + res.end(body); + } + }); +} + +async function callback(nonce: string, reqUrl: url.Url): Promise { + const query = reqUrl.query; + if (!query || typeof query === 'string') { + throw new Error('No query received.'); + } + + let error = query.error_description || query.error; + + if (!error) { + const state = (query.state as string) || ''; + const receivedNonce = (state.split(',')[1] || '').replace(/ /g, '+'); + if (receivedNonce !== nonce) { + error = 'Nonce does not match.'; + } + } + + const code = query.code as string; + if (!error && code) { + return code; + } + + throw new Error((error as string) || 'No code received.'); +} + +export function createServer(nonce: string) { + type RedirectResult = { req: http.IncomingMessage; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; }; + let deferredRedirect: Deferred; + const redirectPromise = new Promise((resolve, reject) => deferredRedirect = { resolve, reject }); + + type CodeResult = { code: string; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; }; + let deferredCode: Deferred; + const codePromise = new Promise((resolve, reject) => deferredCode = { resolve, reject }); + + const codeTimer = setTimeout(() => { + deferredCode.reject(new Error('Timeout waiting for code')); + }, 5 * 60 * 1000); + + function cancelCodeTimer() { + clearTimeout(codeTimer); + } + + const server = http.createServer(function (req, res) { + const reqUrl = url.parse(req.url!, /* parseQueryString */ true); + switch (reqUrl.pathname) { + case '/signin': + const receivedNonce = ((reqUrl.query.nonce as string) || '').replace(/ /g, '+'); + if (receivedNonce === nonce) { + deferredRedirect.resolve({ req, res }); + } else { + const err = new Error('Nonce does not match.'); + deferredRedirect.resolve({ err, res }); + } + break; + case '/': + sendFile(res, path.join(__dirname, '../media/auth.html'), 'text/html; charset=utf-8'); + break; + case '/auth.css': + sendFile(res, path.join(__dirname, '../media/auth.css'), 'text/css; charset=utf-8'); + break; + case '/callback': + deferredCode.resolve(callback(nonce, reqUrl) + .then(code => ({ code, res }), err => ({ err, res }))); + break; + default: + res.writeHead(404); + res.end(); + break; + } + }); + + codePromise.then(cancelCodeTimer, cancelCodeTimer); + return { + server, + redirectPromise, + codePromise + }; +} diff --git a/extensions/microsoft-authentication/src/env/browser/authServer.ts b/extensions/microsoft-authentication/src/env/browser/authServer.ts new file mode 100644 index 0000000000000..60b53c713a85e --- /dev/null +++ b/extensions/microsoft-authentication/src/env/browser/authServer.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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 0000000000000..369a1533cf95d --- /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 { + 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 0000000000000..691f7eedaf939 --- /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 { + 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 0000000000000..102a0863c0e83 --- /dev/null +++ b/extensions/microsoft-authentication/src/extension.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * 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(); + + 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 0000000000000..bc0fcc55381f5 --- /dev/null +++ b/extensions/microsoft-authentication/src/keychain.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. + *--------------------------------------------------------------------------------------------*/ + +// 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 SERVICE_ID = `${vscode.env.uriScheme}-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 { + try { + return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_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 { + try { + return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID); + } catch (e) { + // Ignore + Logger.error(`Getting token failed: ${e}`); + return Promise.resolve(undefined); + } + } + + async deleteToken(): Promise { + try { + return await this.keytar.deletePassword(SERVICE_ID, ACCOUNT_ID); + } catch (e) { + // Ignore + Logger.error(`Deleting token failed: ${e}`); + return Promise.resolve(undefined); + } + } +} + +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 0000000000000..b9ed27bd99379 --- /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 0000000000000..c9849d48e083f --- /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. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/microsoft-authentication/src/utils.ts b/extensions/microsoft-authentication/src/utils.ts new file mode 100644 index 0000000000000..164f2236221b6 --- /dev/null +++ b/extensions/microsoft-authentication/src/utils.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 toBase64UrlEncoding(base64string: string) { + return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding +} diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json new file mode 100644 index 0000000000000..86c288d3c6ee7 --- /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/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock new file mode 100644 index 0000000000000..df970ce40d8c8 --- /dev/null +++ b/extensions/microsoft-authentication/yarn.lock @@ -0,0 +1,611 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/keytar@^4.0.1": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.2.tgz#49ef917d6cbb4f19241c0ab50cd35097b5729b32" + integrity sha512-xtQcDj9ruGnMwvSu1E2BH4SFa5Dv2PvSPd0CKEBLN5hEj/v5YpXJY+B6hAfuKIbvEomD7vJTc/P1s1xPNh2kRw== + 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/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" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +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= + +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: + diagnostic-channel "0.2.0" + diagnostic-channel-publishers "0.2.1" + zone.js "0.7.6" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +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" + resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" + integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A== + dependencies: + readable-stream "^3.0.1" + +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: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +chownr@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +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: + delayed-stream "~1.0.0" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +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" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + +deep-extend@^0.6.0: + version "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" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +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" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +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== + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= + +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= + +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.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== + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +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= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +keytar@*: + version "5.0.0" + resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.0.0.tgz#c89b6b7a4608fd7af633d9f8474b1a7eb97cbe6f" + integrity sha512-a5UheK59YOlJf9i+2Osaj/kkH6mK0RCHVMtJ84u6ZfbfRIbOJ/H4b5VlOF/LgNHF6s78dRSBzZnvIuPiBKv6wg== + dependencies: + 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== + +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.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +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" + +nan@2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +napi-build-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508" + integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA== + +node-abi@^2.7.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.13.0.tgz#e2f2ec444d0aca3ea1b3874b6de41d1665828f63" + integrity sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA== + 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" + integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= + +npmlog@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +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" + +prebuild-install@5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e" + integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.0" + mkdirp "^0.5.1" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" + noop-logger "^0.1.1" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + which-pm-runs "^1.0.0" + +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== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + 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" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^2.0.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@^3.0.1, readable-stream@^3.1.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +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" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +semver@^5.3.0, semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +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= + +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" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + +simple-get@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" + integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + +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" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.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" + +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= + +tar-fs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" + integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== + dependencies: + chownr "^1.1.1" + mkdirp "^0.5.1" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3" + integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw== + dependencies: + bl "^3.0.0" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.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" + +typescript@^3.7.4: + version "3.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19" + integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + 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" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +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 27bb76ffd2456..7700b94ebb04f 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 f3707f85d32a7..144cdd275384f 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: `npm`, `yarn` or `pnpm`, the default is `npm`. - `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 0000000000000..b0c70c96d488f --- /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 d50d9966f639c..1c6d9493e3340 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 0000000000000..f776259aad043 --- /dev/null +++ b/extensions/npm/images/code.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/npm/package.json b/extensions/npm/package.json index cbd9645341a31..aa478b26c9b45 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -18,16 +18,17 @@ "watch": "gulp watch-extension:npm" }, "dependencies": { - "jsonc-parser": "^2.1.1", + "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" }, "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 +40,15 @@ "languages": [ { "id": "ignore", - "filenames": [ + "extensions": [ ".npmignore" ] + }, + { + "id": "properties", + "extensions": [ + ".npmrc" + ] } ], "views": { @@ -49,7 +56,8 @@ { "id": "npm", "name": "%view.name%", - "when": "npm:showScriptExplorer || config.npm.enableScriptExplorer" + "icon": "images/code.svg", + "visibility": "hidden" } ] }, @@ -57,18 +65,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 +83,7 @@ { "command": "npm.refresh", "title": "%command.refresh%", - "icon": { - "light": "resources/light/refresh.svg", - "dark": "resources/dark/refresh.svg" - } + "icon": "$(refresh)" }, { "command": "npm.runSelectedScript", @@ -129,7 +128,7 @@ "editor/context": [ { "command": "npm.runSelectedScript", - "when": "resourceFilename == 'package.json'", + "when": "resourceFilename == 'package.json' && resourceScheme == file", "group": "navigation@+1" } ], @@ -166,14 +165,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 +177,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", @@ -216,7 +210,8 @@ "type": "string", "enum": [ "npm", - "yarn" + "yarn", + "pnpm" ], "default": "npm", "description": "%config.npm.packageManager%" @@ -236,6 +231,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 +264,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 +286,8 @@ "type": "string", "description": "%taskdef.path%" } - } + }, + "when": "shellExecutionSupported" } ] } diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 014010a8425d1..51756d241f14a 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -7,7 +7,7 @@ "config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.", "config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.", "config.npm.scriptExplorerAction": "The default click action used in the npm scripts explorer: `open` or `run`, the default is `open`.", - "config.npm.enableRunFromFolder": "Enable running NPM scripts contained in a folder from the Explorer context menu.", + "config.npm.enableRunFromFolder": "Enable running npm scripts contained in a folder from the Explorer context menu.", "config.npm.fetchOnlinePackageInfo": "Fetch data from https://registry.npmjs.org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies.", "npm.parseError": "Npm task detection: failed to parse the file {0}", "taskdef.script": "The npm script to customize.", diff --git a/extensions/npm/resources/dark/continue.svg b/extensions/npm/resources/dark/continue.svg deleted file mode 100644 index 8b0a58eca9ba4..0000000000000 --- a/extensions/npm/resources/dark/continue.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extensions/npm/resources/dark/debug.svg b/extensions/npm/resources/dark/debug.svg deleted file mode 100644 index e4c1b7a927bc6..0000000000000 --- a/extensions/npm/resources/dark/debug.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/extensions/npm/resources/dark/prepostscript.svg b/extensions/npm/resources/dark/prepostscript.svg deleted file mode 100644 index a8c87f2d8f6b8..0000000000000 --- a/extensions/npm/resources/dark/prepostscript.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/extensions/npm/resources/dark/refresh.svg b/extensions/npm/resources/dark/refresh.svg deleted file mode 100644 index ec0c43f0bc32d..0000000000000 --- a/extensions/npm/resources/dark/refresh.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/extensions/npm/resources/dark/script.svg b/extensions/npm/resources/dark/script.svg deleted file mode 100644 index 7137a9d7bb541..0000000000000 --- a/extensions/npm/resources/dark/script.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extensions/npm/resources/light/continue.svg b/extensions/npm/resources/light/continue.svg deleted file mode 100644 index 2563bfa114bef..0000000000000 --- a/extensions/npm/resources/light/continue.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extensions/npm/resources/light/debug.svg b/extensions/npm/resources/light/debug.svg deleted file mode 100644 index 81a5ffb6b11a2..0000000000000 --- a/extensions/npm/resources/light/debug.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/extensions/npm/resources/light/prepostscript.svg b/extensions/npm/resources/light/prepostscript.svg deleted file mode 100644 index 87eb59e12a65a..0000000000000 --- a/extensions/npm/resources/light/prepostscript.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/extensions/npm/resources/light/refresh.svg b/extensions/npm/resources/light/refresh.svg deleted file mode 100644 index a5b88123a0e4c..0000000000000 --- a/extensions/npm/resources/light/refresh.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/extensions/npm/resources/light/script.svg b/extensions/npm/resources/light/script.svg deleted file mode 100644 index 60f77501db75f..0000000000000 --- a/extensions/npm/resources/light/script.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index 705e40d34e7c8..c3a827fd1e133 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 | 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 { + public collectValueSuggestions(_resource: string, location: Location, collector: ISuggestionsCollector): Promise | 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 | null { @@ -150,10 +155,6 @@ export class BowerJSONContribution implements IJSONContribution { } private getInfo(pack: string): Thenable { - 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 | null { + public getInfoContribution(_resource: string, location: Location): Thenable | 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 e59b7866ec367..071d57b3348d6 100644 --- a/extensions/npm/src/features/jsonContributions.ts +++ b/extensions/npm/src/features/jsonContributions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Location, getLocation, createScanner, SyntaxKind, ScanError } from 'jsonc-parser'; +import { Location, getLocation, createScanner, SyntaxKind, ScanError, JSONScanner } from 'jsonc-parser'; import { basename } from 'path'; import { BowerJSONContribution } from './bowerJSONContribution'; import { PackageJSONContribution } from './packageJSONContribution'; @@ -25,13 +25,13 @@ export interface IJSONContribution { getDocumentSelector(): DocumentSelector; getInfoContribution(fileName: string, location: Location): Thenable | null; collectPropertySuggestions(fileName: string, location: Location, currentWord: string, addValue: boolean, isLast: boolean, result: ISuggestionsCollector): Thenable | null; - collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable; + collectValueSuggestions(fileName: string, location: Location, result: ISuggestionsCollector): Thenable | null; collectDefaultSuggestions(fileName: string, result: ISuggestionsCollector): Thenable; resolveSuggestion?(item: CompletionItem): Thenable | 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(); @@ -111,7 +111,7 @@ export class JSONCompletionItemProvider implements CompletionItemProvider { add: (suggestion: CompletionItem) => { if (!proposed[suggestion.label]) { proposed[suggestion.label] = true; - suggestion.range = overwriteRange; + suggestion.range = { replacing: overwriteRange, inserting: new Range(overwriteRange.start, overwriteRange.start) }; items.push(suggestion); } }, @@ -123,8 +123,9 @@ export class JSONCompletionItemProvider implements CompletionItemProvider { let collectPromise: Thenable | null = null; if (location.isAtPropertyKey) { - const addValue = !location.previousNode || !location.previousNode.colonOffset; - const isLast = this.isLast(document, position); + const scanner = createScanner(document.getText(), true); + const addValue = !location.previousNode || !this.hasColonAfter(scanner, location.previousNode.offset + location.previousNode.length); + const isLast = this.isLast(scanner, document.offsetAt(position)); collectPromise = this.jsonContribution.collectPropertySuggestions(fileName, location, currentWord, addValue, isLast, collector); } else { if (location.path.length === 0) { @@ -153,15 +154,19 @@ export class JSONCompletionItemProvider implements CompletionItemProvider { return text.substring(i + 1, position.character); } - private isLast(document: TextDocument, position: Position): boolean { - const scanner = createScanner(document.getText(), true); - scanner.setPosition(document.offsetAt(position)); + private isLast(scanner: JSONScanner, offset: number): boolean { + scanner.setPosition(offset); let nextToken = scanner.scan(); if (nextToken === SyntaxKind.StringLiteral && scanner.getTokenError() === ScanError.UnexpectedEndOfString) { nextToken = scanner.scan(); } return nextToken === SyntaxKind.CloseBraceToken || nextToken === SyntaxKind.EOF; } + private hasColonAfter(scanner: JSONScanner, offset: number): boolean { + scanner.setPosition(offset); + return scanner.scan() === SyntaxKind.ColonToken; + } + } export const xhrDisabled = () => Promise.reject({ responseText: 'Use of online resources is disabled.' }); diff --git a/extensions/npm/src/features/markedTextUtil.ts b/extensions/npm/src/features/markedTextUtil.ts deleted file mode 100644 index 856fad050e503..0000000000000 --- a/extensions/npm/src/features/markedTextUtil.ts +++ /dev/null @@ -1,10 +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 { MarkedString } from 'vscode'; - -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 diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index 4789886891c47..f154a8752391a 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 { @@ -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 | 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 { - 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 | 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 { - 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 true; } private async fetchPackageInfo(pack: string): Promise { - 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 { return new Promise((resolve, _reject) => { - const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage'; + const command = 'npm view --json ' + pack + ' description dist-tags.latest homepage version'; cp.exec(command, (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 { - 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 | 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/main.ts b/extensions/npm/src/main.ts deleted file mode 100644 index 3777f39375737..0000000000000 --- a/extensions/npm/src/main.ts +++ /dev/null @@ -1,110 +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 httpRequest from 'request-light'; -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 { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; - -let treeDataProvider: NpmScriptsTreeDataProvider | undefined; - -export async function activate(context: vscode.ExtensionContext): Promise { - registerTaskProvider(context); - treeDataProvider = registerExplorer(context); - registerHoverProvider(context); - - configureHttpRequest(); - let d = vscode.workspace.onDidChangeConfiguration((e) => { - configureHttpRequest(); - if (e.affectsConfiguration('npm.exclude')) { - invalidateTasksCache(); - if (treeDataProvider) { - treeDataProvider.refresh(); - } - } - if (e.affectsConfiguration('npm.scriptExplorerAction')) { - if (treeDataProvider) { - treeDataProvider.refresh(); - } - } - }); - context.subscriptions.push(d); - - 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)); -} - -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()); - watcher.onDidDelete((_e) => invalidateScriptCaches()); - watcher.onDidCreate((_e) => invalidateScriptCaches()); - context.subscriptions.push(watcher); - - let workspaceWatcher = vscode.workspace.onDidChangeWorkspaceFolders((_e) => invalidateScriptCaches()); - context.subscriptions.push(workspaceWatcher); - - let provider: vscode.TaskProvider = new NpmTaskProvider(); - let disposable = vscode.workspace.registerTaskProvider('npm', provider); - context.subscriptions.push(disposable); - return disposable; - } - return undefined; -} - -function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataProvider | undefined { - if (vscode.workspace.workspaceFolders) { - let treeDataProvider = new NpmScriptsTreeDataProvider(context); - const view = vscode.window.createTreeView('npm', { treeDataProvider: treeDataProvider, showCollapseAll: true }); - context.subscriptions.push(view); - return treeDataProvider; - } - return undefined; -} - -function registerHoverProvider(context: vscode.ExtensionContext): NpmScriptHoverProvider | undefined { - if (vscode.workspace.workspaceFolders) { - let npmSelector: vscode.DocumentSelector = { - language: 'json', - scheme: 'file', - pattern: '**/package.json' - }; - let provider = new NpmScriptHoverProvider(context); - context.subscriptions.push(vscode.languages.registerHoverProvider(npmSelector, provider)); - return provider; - } - return undefined; -} - -function configureHttpRequest() { - const httpSettings = vscode.workspace.getConfiguration('http'); - httpRequest.configure(httpSettings.get('proxy', ''), httpSettings.get('proxyStrictSSL', true)); -} - -export function deactivate(): void { -} diff --git a/extensions/npm/src/npmBrowserMain.ts b/extensions/npm/src/npmBrowserMain.ts new file mode 100644 index 0000000000000..96cfe57950520 --- /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 { + context.subscriptions.push(addJSONProviders(httpRequest.xhr, false)); +} + +export function deactivate(): void { +} diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts new file mode 100644 index 0000000000000..a92f554b75924 --- /dev/null +++ b/extensions/npm/src/npmMain.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { runSelectedScript, selectAndRunScriptFromFolder } from './commands'; +import { NpmScriptsTreeDataProvider } from './npmView'; +import { invalidateTasksCache, NpmTaskProvider } from './tasks'; +import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; + +let treeDataProvider: NpmScriptsTreeDataProvider | undefined; + +export async function activate(context: vscode.ExtensionContext): Promise { + configureHttpRequest(); + 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(); + } + } + if (e.affectsConfiguration('npm.scriptExplorerAction')) { + if (treeDataProvider) { + treeDataProvider.refresh(); + } + } + })); + + registerTaskProvider(context); + registerHoverProvider(context); + + context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript)); + context.subscriptions.push(vscode.commands.registerCommand('npm.runScriptFromFolder', selectAndRunScriptFromFolder)); +} + +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()); + watcher.onDidDelete((_e) => invalidateScriptCaches()); + watcher.onDidCreate((_e) => invalidateScriptCaches()); + context.subscriptions.push(watcher); + + let workspaceWatcher = vscode.workspace.onDidChangeWorkspaceFolders((_e) => invalidateScriptCaches()); + context.subscriptions.push(workspaceWatcher); + + let provider: vscode.TaskProvider = new NpmTaskProvider(); + let disposable = vscode.tasks.registerTaskProvider('npm', provider); + context.subscriptions.push(disposable); + return disposable; + } + return undefined; +} + +function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataProvider | undefined { + if (vscode.workspace.workspaceFolders) { + let treeDataProvider = new NpmScriptsTreeDataProvider(context); + const view = vscode.window.createTreeView('npm', { treeDataProvider: treeDataProvider, showCollapseAll: true }); + context.subscriptions.push(view); + return treeDataProvider; + } + return undefined; +} + +function registerHoverProvider(context: vscode.ExtensionContext): NpmScriptHoverProvider | undefined { + if (vscode.workspace.workspaceFolders) { + let npmSelector: vscode.DocumentSelector = { + language: 'json', + scheme: 'file', + pattern: '**/package.json' + }; + let provider = new NpmScriptHoverProvider(context); + context.subscriptions.push(vscode.languages.registerHoverProvider(npmSelector, provider)); + return provider; + } + return undefined; +} + +function configureHttpRequest() { + const httpSettings = vscode.workspace.getConfiguration('http'); + httpRequest.configure(httpSettings.get('proxy', ''), httpSettings.get('proxyStrictSSL', true)); +} + +export function deactivate(): void { +} diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index b5234c5fc1b44..c7c7835fa0470 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 * as nls from 'vscode-nls'; import { - NpmTaskDefinition, getPackageJsonUriFromTask, getScripts, - isWorkspaceFolder, getTaskName, createTask, extractDebugArgFromScript, startDebugging + createTask, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, NpmTaskDefinition, + startDebugging } from './tasks'; -import * as nls from 'vscode-nls'; 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('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'; } } @@ -145,27 +137,8 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { 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.name, script.getFolder()); } private findScript(document: TextDocument, script?: NpmScript): number { @@ -231,7 +204,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { public refresh() { this.taskTree = null; - this._onDidChangeTreeData.fire(); + this._onDidChangeTreeData.fire(null); } getTreeItem(element: TreeItem): TreeItem { @@ -260,7 +233,11 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { 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/scriptHover.ts b/extensions/npm/src/scriptHover.ts index 46992af2088b1..f8a5482bef85a 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -8,7 +8,7 @@ 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'; @@ -32,6 +32,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 { @@ -51,11 +54,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 +74,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'), @@ -113,11 +110,9 @@ export class NpmScriptHoverProvider implements HoverProvider { public debugScriptFromHover(args: any) { 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, folder); } } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index c8a1eb852360e..f7def2f8876fb 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { - TaskDefinition, Task2 as Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, + TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem } from 'vscode'; import * as path from 'path'; @@ -29,6 +29,8 @@ type AutoDetect = 'on' | 'off'; let cachedTasks: Task[] | undefined = undefined; +const INSTALL_SCRIPT = 'install'; + export class NpmTaskProvider implements TaskProvider { constructor() { @@ -52,7 +54,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; } @@ -144,7 +146,7 @@ async function detectNpmScripts(): Promise { for (const folder of folders) { if (isAutoDetectionEnabled(folder)) { let relativePattern = new RelativePattern(folder, '**/package.json'); - let paths = await workspace.findFiles(relativePattern, '**/node_modules/**'); + let paths = await workspace.findFiles(relativePattern, '**/{node_modules,.vscode-test}/**'); for (const path of paths) { if (!isExcluded(folder, path) && !visitedPackageJsonFiles.has(path.fsPath)) { let tasks = await provideNpmScriptsForFolder(path); @@ -190,8 +192,8 @@ export async function provideNpmScripts(): Promise { return cachedTasks; } -function isAutoDetectionEnabled(folder: WorkspaceFolder): boolean { - return workspace.getConfiguration('npm', folder.uri).get('autoDetect') === 'on'; +export function isAutoDetectionEnabled(folder?: WorkspaceFolder): boolean { + return workspace.getConfiguration('npm', folder?.uri).get('autoDetect') === 'on'; } function isExcluded(folder: WorkspaceFolder, packageJsonUri: Uri) { @@ -247,13 +249,15 @@ async function provideNpmScriptsForFolder(packageJsonUri: Uri): Promise 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(createTask(INSTALL_SCRIPT, INSTALL_SCRIPT, folder, packageJsonUri, 'install dependencies from package', [])); return result; } @@ -353,44 +357,16 @@ export function runScript(script: string, document: TextDocument) { } } -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 function startDebugging(scriptName: string, folder: WorkspaceFolder) { const config: DebugConfiguration = { - type: 'node', + type: 'pwa-node', request: 'launch', name: `Debug ${scriptName}`, - runtimeExecutable: packageManager, + runtimeExecutable: getPackageManager(folder), runtimeArgs: [ 'run', scriptName, ], - port: port, - protocol: p }; if (folder) { diff --git a/extensions/npm/yarn.lock b/extensions/npm/yarn.lock index 4c6c723c3cb6d..5e85de9297e7c 100644 --- a/extensions/npm/yarn.lock +++ b/extensions/npm/yarn.lock @@ -71,7 +71,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 +79,10 @@ 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== +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== minimatch@^3.0.4: version "3.0.4" @@ -96,21 +96,21 @@ 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== +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" - -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== + https-proxy-agent "^2.2.4" + vscode-nls "^4.1.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-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== diff --git a/extensions/objective-c/cgmanifest.json b/extensions/objective-c/cgmanifest.json index 9ff092c2ab098..592ff960fb619 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/language-configuration.json b/extensions/objective-c/language-configuration.json index e4656d0babb69..a81a8864a5127 100644 --- a/extensions/objective-c/language-configuration.json +++ b/extensions/objective-c/language-configuration.json @@ -12,8 +12,8 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -22,4 +22,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 2510f2dc7438d..473fd1e28b403 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 c4f19d8db544c..63ae3d9970d02 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/objective-c/test/colorize-results/test_m.json b/extensions/objective-c/test/colorize-results/test_m.json index 5923e3433da51..d0a8eb93dd31d 100644 --- a/extensions/objective-c/test/colorize-results/test_m.json +++ b/extensions/objective-c/test/colorize-results/test_m.json @@ -752,9 +752,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc meta.bracket.square.access.objc constant.numeric.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1731,9 +1731,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc keyword.other.unit.hexadecimal.objc", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1742,9 +1742,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.hexadecimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1819,9 +1819,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1830,9 +1830,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.point.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1841,9 +1841,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1896,9 +1896,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1907,9 +1907,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.point.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1918,9 +1918,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1929,9 +1929,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc keyword.other.unit.exponent.decimal.objc", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1940,9 +1940,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.exponent.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1995,9 +1995,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2006,9 +2006,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.point.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2017,9 +2017,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2028,9 +2028,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc keyword.other.unit.exponent.decimal.objc", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -2039,9 +2039,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc keyword.operator.minus.exponent.decimal.objc", "r": { "dark_plus": "keyword.operator.minus.exponent: #B5CEA8", - "light_plus": "keyword.operator.minus.exponent: #09885A", + "light_plus": "keyword.operator.minus.exponent: #098658", "dark_vs": "keyword.operator.minus.exponent: #B5CEA8", - "light_vs": "keyword.operator.minus.exponent: #09885A", + "light_vs": "keyword.operator.minus.exponent: #098658", "hc_black": "keyword.operator: #D4D4D4" } }, @@ -2050,9 +2050,9 @@ "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.objc constant.numeric.exponent.decimal.objc", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3090,4 +3090,4 @@ "hc_black": "storage.type: #569CD6" } } -] \ No newline at end of file +] diff --git a/extensions/objective-c/test/colorize-results/test_mm.json b/extensions/objective-c/test/colorize-results/test_mm.json index 6134c33a619be..e724c6022ffc8 100644 --- a/extensions/objective-c/test/colorize-results/test_mm.json +++ b/extensions/objective-c/test/colorize-results/test_mm.json @@ -752,9 +752,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp meta.bracket.square.access.objcpp constant.numeric.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1731,9 +1731,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.other.unit.hexadecimal.objcpp", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1742,9 +1742,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.hexadecimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1819,9 +1819,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1830,9 +1830,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.point.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1841,9 +1841,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1896,9 +1896,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1907,9 +1907,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.point.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1918,9 +1918,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1929,9 +1929,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.other.unit.exponent.decimal.objcpp", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1940,9 +1940,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.exponent.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1995,9 +1995,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2006,9 +2006,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.point.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2017,9 +2017,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2028,9 +2028,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.other.unit.exponent.decimal.objcpp", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -2039,9 +2039,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp keyword.operator.minus.exponent.decimal.objcpp", "r": { "dark_plus": "keyword.operator.minus.exponent: #B5CEA8", - "light_plus": "keyword.operator.minus.exponent: #09885A", + "light_plus": "keyword.operator.minus.exponent: #098658", "dark_vs": "keyword.operator.minus.exponent: #B5CEA8", - "light_vs": "keyword.operator.minus.exponent: #09885A", + "light_vs": "keyword.operator.minus.exponent: #098658", "hc_black": "keyword.operator: #D4D4D4" } }, @@ -2050,9 +2050,9 @@ "t": "source.objcpp meta.implementation.objcpp meta.scope.implementation.objcpp meta.function-with-body.objcpp meta.block.objcpp constant.numeric.exponent.decimal.objcpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3090,4 +3090,4 @@ "hc_black": "storage.type: #569CD6" } } -] \ No newline at end of file +] diff --git a/extensions/package.json b/extensions/package.json index 688a5c05063c8..97870c8d51b8f 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.2" + "typescript": "4.0.2" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/perl/perl.language-configuration.json b/extensions/perl/perl.language-configuration.json index 6cf6295b7985e..4d18f1fbaa485 100644 --- a/extensions/perl/perl.language-configuration.json +++ b/extensions/perl/perl.language-configuration.json @@ -11,9 +11,9 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["`", "`"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] }, + { "open": "`", "close": "`", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -25,8 +25,8 @@ ], "folding": { "markers": { - "start": "^=pod\\s*$", - "end": "^=cut\\s*$" + "start": "^(?:(?:=pod\\s*$)|(?:\\s*#region\\b))", + "end": "^(?:(?:=cut\\s*$)|(?:\\s*#endregion\\b))" } } -} \ No newline at end of file +} diff --git a/extensions/perl/perl6.language-configuration.json b/extensions/perl/perl6.language-configuration.json index 01b6a8a28239e..be52105cbdf14 100644 --- a/extensions/perl/perl6.language-configuration.json +++ b/extensions/perl/perl6.language-configuration.json @@ -11,9 +11,9 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["`", "`"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] }, + { "open": "`", "close": "`", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -23,4 +23,4 @@ ["'", "'"], ["`", "`"] ] -} \ No newline at end of file +} diff --git a/extensions/perl/test/colorize-results/test2_pl.json b/extensions/perl/test/colorize-results/test2_pl.json index 99e22463982cb..6fc00b0b06425 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 ecfd9660ad88e..e47c0f95e841e 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 d301ca476ea98..2856782f547d8 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/validationProvider.ts b/extensions/php-language-features/src/features/validationProvider.ts index 122c108ac378b..5c9d34afb5944 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/cgmanifest.json b/extensions/php/cgmanifest.json index 71caf1a84da4f..3c5b9b9ffd3d4 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": "43a7b842fb58ac7561184db142344a1b127e0d52" + "commitHash": "6c0da475f86b45ea990638525f68be7658986546" } }, "license": "MIT", - "version": "0.44.2" + "version": "0.44.5" } ], "version": 1 diff --git a/extensions/php/package.json b/extensions/php/package.json index 2d0e4204ec104..0bd740a10adbd 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.code-snippets b/extensions/php/snippets/php.code-snippets new file mode 100644 index 0000000000000..b2e9078d91f34 --- /dev/null +++ b/extensions/php/snippets/php.code-snippets @@ -0,0 +1,263 @@ +{ + "class …": { + "prefix": "class", + "body": [ + "class ${1:ClassName} ${2:extends ${3:AnotherClass}} ${4:implements ${5:Interface}}", + "{", + "\t$0", + "}", + "" + ], + "description": "Class definition" + }, + "PHPDoc class …": { + "prefix": "doc_class", + "body": [ + "/**", + " * ${6:undocumented class}", + " */", + "class ${1:ClassName} ${2:extends ${3:AnotherClass}} ${4:implements ${5:Interface}}", + "{", + "\t$0", + "}", + "" + ], + "description": "Documented Class Declaration" + }, + "function __construct": { + "prefix": "con", + "body": [ + "${1:public} function __construct(${2:${3:Type} $${4:var}${5: = ${6:null}}}) {", + "\t\\$this->${4:var} = $${4:var};$0", + "}" + ] + }, + "PHPDoc property": { + "prefix": "doc_v", + "body": [ + "/** @var ${1:Type} $${2:var} ${3:description} */", + "${4:protected} $${2:var}${5: = ${6:null}};$0" + ], + "description": "Documented Class Variable" + }, + "PHPDoc function …": { + "prefix": "doc_f", + "body": [ + "/**", + " * ${1:undocumented function summary}", + " *", + " * ${2:Undocumented function long description}", + " *", + "${3: * @param ${4:Type} $${5:var} ${6:Description}}", + "${7: * @return ${8:type}}", + "${9: * @throws ${10:conditon}}", + " **/", + "${11:public }function ${12:FunctionName}(${13:${14:${4:Type} }$${5:var}${15: = ${16:null}}})", + "{", + "\t${0:# code...}", + "}" + ], + "description": "Documented function" + }, + "PHPDoc param …": { + "prefix": "param", + "body": [ + "* @param ${1:Type} ${2:var} ${3:Description}$0" + ], + "description": "Paramater documentation" + }, + "function …": { + "prefix": "fun", + "body": [ + "${1:public }function ${2:FunctionName}(${3:${4:${5:Type} }$${6:var}${7: = ${8:null}}})", + "{", + "\t${0:# code...}", + "}" + ], + "description": "Function" + }, + "trait …": { + "prefix": "trait", + "body": [ + "/**", + " * $1", + " */", + "trait ${2:TraitName}", + "{", + "\t$0", + "}", + "" + ], + "description": "Trait" + }, + "define(…, …)": { + "prefix": "def", + "body": [ + "define('$1', ${2:'$3'});", + "$0" + ], + "description": "Definition" + }, + "do … while …": { + "prefix": "do", + "body": [ + "do {", + "\t${0:# code...}", + "} while (${1:$${2:a} <= ${3:10}});" + ], + "description": "Do-While loop" + }, + "while …": { + "prefix": "while", + "body": [ + "while (${1:$${2:a} <= ${3:10}}) {", + "\t${0:# code...}", + "}" + ], + "description": "While-loop" + }, + "if …": { + "prefix": "if", + "body": [ + "if (${1:condition}) {", + "\t${0:# code...}", + "}" + ], + "description": "If block" + }, + "if … else …": { + "prefix": "ifelse", + "body": [ + "if (${1:condition}) {", + "\t${2:# code...}", + "} else {", + "\t${3:# code...}", + "}", + "$0" + ], + "description": "If Else block" + }, + "$… = ( … ) ? … : …": { + "prefix": "if?", + "body": "$${1:retVal} = (${2:condition}) ? ${3:a} : ${4:b} ;", + "description": "Ternary conditional assignment" + }, + "else …": { + "prefix": "else", + "body": [ + "else {", + "\t${0:# code...}", + "}" + ], + "description": "Else block" + }, + "elseif …": { + "prefix": "elseif", + "body": [ + "elseif (${1:condition}) {", + "\t${0:# code...}", + "}" + ], + "description": "Elseif block" + }, + "for …": { + "prefix": "for", + "body": [ + "for ($${1:i}=${2:0}; $${1:i} < $3; $${1:i}++) { ", + "\t${0:# code...}", + "}" + ], + "description": "For-loop" + }, + "foreach …": { + "prefix": "foreach", + "body": [ + "foreach ($${1:variable} as $${2:key} ${3:=> $${4:value}}) {", + "\t${0:# code...}", + "}" + ], + "description": "Foreach loop" + }, + "$… = array (…)": { + "prefix": "array", + "body": "$${1:arrayName} = array('$2' => $3${4:,} $0);", + "description": "Array initializer" + }, + "$… = […]": { + "prefix": "shorray", + "body": "$${1:arrayName} = ['$2' => $3${4:,} $0];", + "description": "Array initializer" + }, + "… => …": { + "prefix": "keyval", + "body": "'$1' => $2${3:,} $0", + "description": "Key-Value initializer" + }, + "switch …": { + "prefix": "switch", + "body": [ + "switch (\\$${1:variable}) {", + "\tcase '${2:value}':", + "\t\t${3:# code...}", + "\t\tbreak;", + "\t$0", + "\tdefault:", + "\t\t${4:# code...}", + "\t\tbreak;", + "}" + ], + "description": "Switch block" + }, + "case …": { + "prefix": "case", + "body": [ + "case '${1:value}':", + "\t${0:# code...}", + "\tbreak;" + ], + "description": "Case Block" + }, + "$this->…": { + "prefix": "this", + "body": "\\$this->$0;", + "description": "$this->..." + }, + "echo $this->…": { + "prefix": "ethis", + "body": "echo \\$this->$0;", + "description": "Echo this" + }, + "Throw Exception": { + "prefix": "throw", + "body": [ + "throw new $1Exception(${2:\"${3:Error Processing Request}\"}${4:, ${5:1}});", + "$0" + ], + "description": "Throw exception" + }, + "Region Start": { + "prefix": "#region", + "body": [ + "#region" + ], + "description": "Folding Region Start" + }, + "Region End": { + "prefix": "#endregion", + "body": [ + "#endregion" + ], + "description": "Folding Region End" + }, + "Try Catch Block": { + "prefix": "try", + "body": [ + "try {", + "\t${1://code...}", + "} catch (${2:\\Throwable} ${3:\\$th}) {", + "\t${4://throw \\$th;}", + "}" + ], + "description": "Try catch block" + } +} diff --git a/extensions/php/snippets/php.snippets.json b/extensions/php/snippets/php.snippets.json deleted file mode 100644 index a0705e3aed421..0000000000000 --- a/extensions/php/snippets/php.snippets.json +++ /dev/null @@ -1,263 +0,0 @@ -{ - "class …": { - "prefix": "class", - "body": [ - "class ${1:ClassName} ${2:extends ${3:AnotherClass}} ${4:implements ${5:Interface}}", - "{", - "\t$0", - "}", - "" - ], - "description": "Class definition" - }, - "PHPDoc class …": { - "prefix": "doc_class", - "body": [ - "/**", - " * ${6:undocumented class}", - " */", - "class ${1:ClassName} ${2:extends ${3:AnotherClass}} ${4:implements ${5:Interface}}", - "{", - "\t$0", - "}", - "" - ], - "description": "Documented Class Declaration" - }, - "function __construct": { - "prefix": "con", - "body": [ - "${1:public} function __construct(${2:${3:Type} $${4:var}${5: = ${6:null}}}) {", - "\t\\$this->${4:var} = $${4:var};$0", - "}" - ] - }, - "PHPDoc property": { - "prefix": "doc_v", - "body": [ - "/** @var ${1:Type} $${2:var} ${3:description} */", - "${4:protected} $${2:var}${5: = ${6:null}};$0" - ], - "description": "Documented Class Variable" - }, - "PHPDoc function …": { - "prefix": "doc_f", - "body": [ - "/**", - " * ${1:undocumented function summary}", - " *", - " * ${2:Undocumented function long description}", - " *", - "${3: * @param ${4:Type} $${5:var} ${6:Description}}", - "${7: * @return ${8:type}}", - "${9: * @throws ${10:conditon}}", - " **/", - "${11:public }function ${12:FunctionName}(${13:${14:${4:Type} }$${5:var}${15: = ${16:null}}})", - "{", - "\t${0:# code...}", - "}" - ], - "description": "Documented function" - }, - "PHPDoc param …": { - "prefix": "param", - "body": [ - "* @param ${1:Type} ${2:var} ${3:Description}$0" - ], - "description": "Paramater documentation" - }, - "function …": { - "prefix": "fun", - "body": [ - "${1:public }function ${2:FunctionName}(${3:${4:${5:Type} }$${6:var}${7: = ${8:null}}})", - "{", - "\t${0:# code...}", - "}" - ], - "description": "Function" - }, - "trait …": { - "prefix": "trait", - "body": [ - "/**", - " * $1", - " */", - "trait ${2:TraitName}", - "{", - "\t$0", - "}", - "" - ], - "description": "Trait" - }, - "define(…, …)": { - "prefix": "def", - "body": [ - "define('$1', ${2:'$3'});", - "$0" - ], - "description": "Definition" - }, - "do … while …": { - "prefix": "do", - "body": [ - "do {", - "\t${0:# code...}", - "} while (${1:$${2:a} <= ${3:10}});" - ], - "description": "Do-While loop" - }, - "while …": { - "prefix": "while", - "body": [ - "while (${1:$${2:a} <= ${3:10}}) {", - "\t${0:# code...}", - "}" - ], - "description": "While-loop" - }, - "if …": { - "prefix": "if", - "body": [ - "if (${1:condition}) {", - "\t${0:# code...}", - "}" - ], - "description": "If block" - }, - "if … else …": { - "prefix": "ifelse", - "body": [ - "if (${1:condition}) {", - "\t${2:# code...}", - "} else {", - "\t${3:# code...}", - "}", - "$0" - ], - "description": "If Else block" - }, - "$… = ( … ) ? … : …": { - "prefix": "if?", - "body": "$${1:retVal} = (${2:condition}) ? ${3:a} : ${4:b} ;", - "description": "Ternary conditional assignment" - }, - "else …": { - "prefix": "else", - "body": [ - "else {", - "\t${0:# code...}", - "}" - ], - "description": "Else block" - }, - "elseif …": { - "prefix": "elseif", - "body": [ - "elseif (${1:condition}) {", - "\t${0:# code...}", - "}" - ], - "description": "Elseif block" - }, - "for …": { - "prefix": "for", - "body": [ - "for ($${1:i}=${2:0}; $${1:i} < $3; $${1:i}++) { ", - "\t${0:# code...}", - "}" - ], - "description": "For-loop" - }, - "foreach …": { - "prefix": "foreach", - "body": [ - "foreach ($${1:variable} as $${2:key} ${3:=> $${4:value}}) {", - "\t${0:# code...}", - "}" - ], - "description": "Foreach loop" - }, - "$… = array (…)": { - "prefix": "array", - "body": "$${1:arrayName} = array('$2' => $3${4:,} $0);", - "description": "Array initializer" - }, - "$… = […]": { - "prefix": "shorray", - "body": "$${1:arrayName} = ['$2' => $3${4:,} $0];", - "description": "Array initializer" - }, - "… => …": { - "prefix": "keyval", - "body": "'$1' => $2${3:,} $0", - "description": "Key-Value initializer" - }, - "switch …": { - "prefix": "switch", - "body": [ - "switch (\\$${1:variable}) {", - "\tcase '${2:value}':", - "\t\t${3:# code...}", - "\t\tbreak;", - "\t$0", - "\tdefault:", - "\t\t${4:# code...}", - "\t\tbreak;", - "}" - ], - "description": "Switch block" - }, - "case …": { - "prefix": "case", - "body": [ - "case '${1:value}':", - "\t${0:# code...}", - "\tbreak;" - ], - "description": "Case Block" - }, - "$this->…": { - "prefix": "this", - "body": "\\$this->$0;", - "description": "$this->..." - }, - "echo $this->…": { - "prefix": "ethis", - "body": "echo \\$this->$0;", - "description": "Echo this" - }, - "Throw Exception": { - "prefix": "throw", - "body": [ - "throw new $1Exception(${2:\"${3:Error Processing Request}\"}${4:, ${5:1}});", - "$0" - ], - "description": "Throw exception" - }, - "Region Start": { - "prefix": "#region", - "body": [ - "#region" - ], - "description": "Folding Region Start" - }, - "Region End": { - "prefix": "#endregion", - "body": [ - "#endregion" - ], - "description": "Folding Region End" - }, - "Try Catch Block": { - "prefix": "try", - "body": [ - "try {", - "\t${1://code...}", - "} catch (${2:\\Throwable} ${3:\\$th}) {", - "\t${4://throw \\$th;}", - "}" - ], - "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 2b9370f917cfb..54fbd4b7869df 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/43a7b842fb58ac7561184db142344a1b127e0d52", + "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": "}|(?=\\?>)", @@ -439,22 +445,25 @@ "name": "keyword.control.exception.php" }, { - "begin": "(?i)\\b(function)\\s*(?=\\()", + "begin": "(?i)\\b(function)\\s*(?=&?\\s*\\()", "beginCaptures": { "1": { "name": "storage.type.function.php" } }, - "end": "(?={)", + "end": "(?=\\s*{)", "name": "meta.function.closure.php", "patterns": [ { "include": "#comments" }, { - "begin": "\\(", + "begin": "(&)?\\s*(\\()", "beginCaptures": { - "0": { + "1": { + "name": "storage.modifier.reference.php" + }, + "2": { "name": "punctuation.definition.parameters.begin.bracket.round.php" } }, @@ -504,11 +513,79 @@ "name": "meta.function.closure.use.php" } ] + }, + { + "match": "(:)\\s*(\\?)?\\s*((?:\\\\?[a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*)+)", + "captures": { + "1": { + "name": "keyword.operator.return-value.php" + }, + "2": { + "name": "keyword.operator.nullable-type.php" + }, + "3": { + "name": "storage.type.php" + } + } + } + ] + }, + { + "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))\n |([a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*)\n)\n\\s*(\\()", + "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": [ @@ -525,14 +602,17 @@ "name": "support.function.magic.php" }, "4": { - "name": "entity.name.function.php" + "name": "storage.modifier.reference.php" }, "5": { + "name": "entity.name.function.php" + }, + "6": { "name": "punctuation.definition.parameters.begin.bracket.round.php" } }, "contentName": "meta.function.parameters.php", - "end": "(\\))(?:\\s*(:)\\s*(\\?)?\\s*([a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*))?", + "end": "(\\))(?:\\s*(:)\\s*(\\?)?\\s*((?:\\\\?[a-zA-Z_\\x{7f}-\\x{7fffffff}][a-zA-Z0-9_\\x{7f}-\\x{7fffffff}]*)+))?", "endCaptures": { "1": { "name": "punctuation.definition.parameters.end.bracket.round.php" @@ -554,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" }, @@ -647,6 +765,10 @@ "match": "(?i)\\bclone\\b", "name": "keyword.other.clone.php" }, + { + "match": "\\.\\.\\.", + "name": "keyword.operator.spread.php" + }, { "match": "\\.=?", "name": "keyword.operator.string.php" @@ -678,7 +800,7 @@ "name": "keyword.operator.comparison.php" }, { - "match": "=|\\+=|\\-=|\\*=|/=|%=|&=|\\|=|\\^=|<<=|>>=", + "match": "=|\\+=|\\-=|\\*\\*?=|/=|%=|&=|\\|=|\\^=|<<=|>>=|\\?\\?=", "name": "keyword.operator.assignment.php" }, { @@ -690,7 +812,7 @@ "name": "keyword.operator.increment-decrement.php" }, { - "match": "\\-|\\+|\\*|/|%", + "match": "\\-|\\+|\\*\\*?|/|%", "name": "keyword.operator.arithmetic.php" }, { @@ -711,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" @@ -1207,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": [ { @@ -1235,6 +1368,9 @@ }, "name": "meta.function-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -1273,6 +1409,9 @@ }, "name": "meta.function-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -1939,7 +2078,7 @@ ] }, "instantiation": { - "begin": "(?i)(new)\\s+", + "begin": "(?i)(new)\\s+(?!class\\b)", "beginCaptures": { "1": { "name": "keyword.other.new.php" @@ -2046,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": { @@ -2070,7 +2209,7 @@ } }, { - "match": "0|[1-9][0-9]*", + "match": "0|[1-9](?:_?[0-9]+)*", "name": "constant.numeric.decimal.php" } ] @@ -2120,6 +2259,9 @@ }, "name": "meta.method-call.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -2492,6 +2634,9 @@ }, "name": "meta.method-call.static.php", "patterns": [ + { + "include": "#named-arguments" + }, { "include": "$self" } @@ -3325,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 3a34ef9deb8db..eb36ede3a4eae 100644 --- a/extensions/php/test/colorize-results/issue-28354_php.json +++ b/extensions/php/test/colorize-results/issue-28354_php.json @@ -1,4 +1,5 @@ -[{ +[ + { "c": "<", "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { @@ -278,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" @@ -377,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" @@ -495,7 +496,7 @@ }, { "c": "<", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js-ignored-vscode", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/php/test/colorize-results/test_php.json b/extensions/php/test/colorize-results/test_php.json index 277428ac11b70..62c1b712679c9 100644 --- a/extensions/php/test/colorize-results/test_php.json +++ b/extensions/php/test/colorize-results/test_php.json @@ -1137,9 +1137,9 @@ "t": "text.html.php meta.embedded.block.php source.php constant.numeric.decimal.php", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1225,9 +1225,9 @@ "t": "text.html.php meta.embedded.block.php source.php constant.numeric.decimal.php", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2490,9 +2490,9 @@ "t": "text.html.php meta.embedded.block.php source.php constant.numeric.decimal.php", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2776,9 +2776,9 @@ "t": "text.html.php meta.embedded.block.php source.php constant.numeric.decimal.php", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2864,9 +2864,9 @@ "t": "text.html.php meta.embedded.block.php source.php constant.numeric.decimal.php", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3062,9 +3062,9 @@ "t": "text.html.php meta.embedded.block.php source.php constant.numeric.decimal.php", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3172,9 +3172,9 @@ "t": "text.html.php meta.embedded.block.php source.php constant.numeric.decimal.php", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3629,4 +3629,4 @@ "hc_black": "punctuation.definition.tag: #808080" } } -] \ No newline at end of file +] diff --git a/extensions/postinstall.js b/extensions/postinstall.js index e31009ac6b84a..da4fa3e9d0443 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'); + +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); + } } +} + +function processLib() { + const toDelete = new Set([ + 'tsc.js', + 'tsserverlibrary.js', + 'typescriptServices.js', + ]); + + const libRoot = path.join(root, 'lib'); - 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); + 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); + } } } -} \ No newline at end of file +} + +processRoot(); +processLib(); diff --git a/extensions/powershell/language-configuration.json b/extensions/powershell/language-configuration.json index f30c0f5f0510f..719b5f81b5258 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 232acb1a7d283..fb45b704b8594 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/powershell/test/colorize-results/test_ps1.json b/extensions/powershell/test/colorize-results/test_ps1.json index 67380ce77b6ff..d12f004fb9faf 100644 --- a/extensions/powershell/test/colorize-results/test_ps1.json +++ b/extensions/powershell/test/colorize-results/test_ps1.json @@ -950,9 +950,9 @@ "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell meta.attribute.powershell constant.numeric.integer.powershell", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1522,9 +1522,9 @@ "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell constant.numeric.integer.powershell", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1599,9 +1599,9 @@ "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell constant.numeric.integer.powershell", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2892,4 +2892,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/pug/language-configuration.json b/extensions/pug/language-configuration.json index 00f4885d46e8a..98a2b77ca27d3 100644 --- a/extensions/pug/language-configuration.json +++ b/extensions/pug/language-configuration.json @@ -11,8 +11,8 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["'", "'"], - ["\"", "\""] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -24,4 +24,4 @@ "folding": { "offSide": true } -} \ No newline at end of file +} diff --git a/extensions/pug/package.json b/extensions/pug/package.json index 19049be77af94..7565f0a0119a8 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/pug/test/colorize-results/test_pug.json b/extensions/pug/test/colorize-results/test_pug.json index a022fd80cd057..b23f1fca31f0d 100644 --- a/extensions/pug/test/colorize-results/test_pug.json +++ b/extensions/pug/test/colorize-results/test_pug.json @@ -158,9 +158,9 @@ "t": "text.pug meta.tag.other attribute_value constant.numeric.decimal.js", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -180,9 +180,9 @@ "t": "text.pug meta.tag.other attribute_value constant.numeric.decimal.js", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -455,9 +455,9 @@ "t": "text.pug meta.tag.other attribute_value constant.numeric.decimal.js", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -477,9 +477,9 @@ "t": "text.pug meta.tag.other attribute_value constant.numeric.decimal.js", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1704,4 +1704,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/python/.vscodeignore b/extensions/python/.vscodeignore index 4d5a14fc91e36..b5c95d0fb6417 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 3ee82895a82aa..37a21b2de54cb 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/python/extension-browser.webpack.config.js b/extensions/python/extension-browser.webpack.config.js new file mode 100644 index 0000000000000..9ffa8d167e932 --- /dev/null +++ b/extensions/python/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/pythonMain.ts' + }, + output: { + filename: 'pythonMain.js' + } +}); diff --git a/extensions/python/package.json b/extensions/python/package.json index 645cba2bae386..f39c3d62b23bf 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -8,13 +8,15 @@ "engines": { "vscode": "*" }, "activationEvents": ["onLanguage:python"], "main": "./out/pythonMain", + "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" ], - "firstLine": "^#!\\s*/.*\\bpython[0-9.-]*\\b", + "filenames": [ "Snakefile", "SConstruct", "SConscript" ], + "firstLine": "^#!\\s*/?.*\\bpython[0-9.-]*\\b", "configuration": "./language-configuration.json" }], "grammars": [{ diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index f59618f75ff7e..0df9076dfc91f 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 c7e67436a08d6..fc11fa5affea4 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 63889e26feeb2..3eb0f85a2a183 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 2c3126586250e..c0a14453646f8 100644 --- a/extensions/python/test/colorize-results/test_py.json +++ b/extensions/python/test/colorize-results/test_py.json @@ -169,9 +169,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -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", @@ -565,9 +565,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -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" } }, { @@ -1137,9 +1137,9 @@ "t": "source.python meta.function.python meta.function.parameters.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1214,9 +1214,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1280,9 +1280,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1324,9 +1324,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1390,9 +1390,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1456,9 +1456,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1522,9 +1522,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1566,9 +1566,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1632,9 +1632,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1698,9 +1698,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1764,9 +1764,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1808,9 +1808,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1874,9 +1874,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1962,9 +1962,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -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" } }, { @@ -2424,9 +2424,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2446,9 +2446,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2468,9 +2468,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2490,9 +2490,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2985,9 +2985,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3359,9 +3359,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3414,9 +3414,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3480,9 +3480,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3568,9 +3568,9 @@ "t": "source.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3601,9 +3601,9 @@ "t": "source.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3656,9 +3656,9 @@ "t": "source.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3722,9 +3722,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3854,9 +3854,9 @@ "t": "source.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3898,9 +3898,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -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", @@ -6252,9 +6252,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, diff --git a/extensions/r/cgmanifest.json b/extensions/r/cgmanifest.json index 0781a1507229b..62f2751e6c908 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/language-configuration.json b/extensions/r/language-configuration.json index 6293d4d61a694..dd691e2a6d4c4 100644 --- a/extensions/r/language-configuration.json +++ b/extensions/r/language-configuration.json @@ -11,12 +11,14 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + ["\"", "\""], + ["'", "'"] ] -} \ No newline at end of file +} diff --git a/extensions/r/syntaxes/r.tmLanguage.json b/extensions/r/syntaxes/r.tmLanguage.json index db37b8421fe1e..ad947f6284997 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 2cba70d079bfc..3ecd0091ce5a6 100644 --- a/extensions/r/test/colorize-results/test_r.json +++ b/extensions/r/test/colorize-results/test_r.json @@ -554,9 +554,9 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -598,9 +598,9 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -631,15 +631,15 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, { "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", @@ -675,9 +675,9 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -708,15 +708,15 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, { "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", diff --git a/extensions/razor/test/colorize-results/test_cshtml.json b/extensions/razor/test/colorize-results/test_cshtml.json index 37e1a3d66d12f..e542974ce4268 100644 --- a/extensions/razor/test/colorize-results/test_cshtml.json +++ b/extensions/razor/test/colorize-results/test_cshtml.json @@ -92,9 +92,9 @@ "t": "text.html.cshtml constant.numeric.decimal.cs", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3695,4 +3695,4 @@ "hc_black": "punctuation.definition.tag: #808080" } } -] \ No newline at end of file +] diff --git a/extensions/ruby/language-configuration.json b/extensions/ruby/language-configuration.json index 47c434deffafa..81fdee540f216 100644 --- a/extensions/ruby/language-configuration.json +++ b/extensions/ruby/language-configuration.json @@ -12,15 +12,17 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] }, + { "open": "`", "close": "`", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], ["\"", "\""], - ["'", "'"] + ["'", "'"], + ["`", "`"] ], "indentationRules": { "increaseIndentPattern": "^\\s*((begin|class|(private|protected)\\s+def|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while|case)|([^#]*\\sdo\\b)|([^#]*=\\s*(case|if|unless)))\\b([^#\\{;]|(\"|'|\/).*\\4)*(#.*)?$", diff --git a/extensions/ruby/test/colorize-results/test_rb.json b/extensions/ruby/test/colorize-results/test_rb.json index c53ee5970e20f..c5122336ae1fd 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" @@ -2017,9 +2017,9 @@ "t": "source.ruby constant.numeric.integer.ruby", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -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" @@ -2666,9 +2666,9 @@ "t": "source.ruby constant.numeric.integer.ruby", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, diff --git a/extensions/rust/language-configuration.json b/extensions/rust/language-configuration.json index bd4cbb8d8f079..5d4f16d26a650 100644 --- a/extensions/rust/language-configuration.json +++ b/extensions/rust/language-configuration.json @@ -12,14 +12,13 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + { "open": "\"", "close": "\"", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"] + ["\"", "\""] ], "indentationRules": { "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$", diff --git a/extensions/scss/cgmanifest.json b/extensions/scss/cgmanifest.json index 7d152d3d3f3aa..12247769ce224 100644 --- a/extensions/scss/cgmanifest.json +++ b/extensions/scss/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "atom/language-sass", "repositoryUrl": "https://github.com/atom/language-sass", - "commitHash": "d01d29191ab323fb3cf8bde9df0429f8e07902ff" + "commitHash": "f52ab12f7f9346cc2568129d8c4419bd3d506b47" } }, "license": "MIT", "description": "The file syntaxes/scss.json was derived from the Atom package https://github.com/atom/language-sass which was originally converted from the TextMate bundle https://github.com/alexsancho/SASS.tmbundle.", - "version": "0.62.0" + "version": "0.62.1" } ], "version": 1 diff --git a/extensions/scss/syntaxes/scss.tmLanguage.json b/extensions/scss/syntaxes/scss.tmLanguage.json index 5add08181f65a..21ed870a5cc3e 100644 --- a/extensions/scss/syntaxes/scss.tmLanguage.json +++ b/extensions/scss/syntaxes/scss.tmLanguage.json @@ -4,13 +4,19 @@ "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-sass/commit/d01d29191ab323fb3cf8bde9df0429f8e07902ff", + "version": "https://github.com/atom/language-sass/commit/f52ab12f7f9346cc2568129d8c4419bd3d506b47", "name": "SCSS", "scopeName": "source.css.scss", "patterns": [ { "include": "#variable_setting" }, + { + "include": "#at_rule_forward" + }, + { + "include": "#at_rule_use" + }, { "include": "#at_rule_include" }, @@ -239,6 +245,55 @@ } ] }, + "at_rule_forward": { + "begin": "\\s*((@)forward\\b)\\s*", + "captures": { + "1": { + "name": "keyword.control.at-rule.forward.scss" + }, + "2": { + "name": "punctuation.definition.keyword.scss" + } + }, + "end": "\\s*(?=;)", + "name": "meta.at-rule.forward.scss", + "patterns": [ + { + "match": "\\b(as|hide|show)\\b", + "name": "keyword.control.operator" + }, + { + "match": "\\b([\\w-]+)(\\*)", + "captures": { + "1": { + "name": "entity.other.attribute-name.module.scss" + }, + "2": { + "name": "punctuation.definition.wildcard.scss" + } + } + }, + { + "match": "\\b[\\w-]+\\b", + "name": "entity.name.function.scss" + }, + { + "include": "#variable" + }, + { + "include": "#string_single" + }, + { + "include": "#string_double" + }, + { + "include": "#comment_line" + }, + { + "include": "#comment_block" + } + ] + }, "at_rule_function": { "patterns": [ { @@ -336,12 +391,18 @@ "at_rule_include": { "patterns": [ { - "begin": "(?<=@include)\\s+([\\w-]+)\\s*(\\()", + "begin": "(?<=@include)\\s+(?:([\\w-]+)\\s*(\\.))?([\\w-]+)\\s*(\\()", "beginCaptures": { "1": { - "name": "entity.name.function.scss" + "name": "variable.scss" }, "2": { + "name": "punctuation.access.module.scss" + }, + "3": { + "name": "entity.name.function.scss" + }, + "4": { "name": "punctuation.definition.parameters.begin.bracket.round.scss" } }, @@ -359,12 +420,18 @@ ] }, { - "match": "(?<=@include)\\s+([\\w-]+)", + "match": "(?<=@include)\\s+(?:([\\w-]+)\\s*(\\.))?([\\w-]+)", "captures": { "0": { "name": "meta.at-rule.include.scss" }, "1": { + "name": "variable.scss" + }, + "2": { + "name": "punctuation.access.module.scss" + }, + "3": { "name": "entity.name.function.scss" } } @@ -794,6 +861,64 @@ } ] }, + "at_rule_use": { + "begin": "\\s*((@)use\\b)\\s*", + "captures": { + "1": { + "name": "keyword.control.at-rule.use.scss" + }, + "2": { + "name": "punctuation.definition.keyword.scss" + } + }, + "end": "\\s*(?=;)", + "name": "meta.at-rule.use.scss", + "patterns": [ + { + "match": "\\b(as|with)\\b", + "name": "keyword.control.operator" + }, + { + "match": "\\b[\\w-]+\\b", + "name": "variable.scss" + }, + { + "match": "\\*", + "name": "variable.language.expanded-namespace.scss" + }, + { + "include": "#string_single" + }, + { + "include": "#string_double" + }, + { + "include": "#comment_line" + }, + { + "include": "#comment_block" + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.parameters.begin.bracket.round.scss" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.parameters.end.bracket.round.scss" + } + }, + "patterns": [ + { + "include": "#function_attributes" + } + ] + } + ] + }, "at_rule_warn": { "begin": "\\s*((@)(warn|debug|error)\\b)\\s*", "captures": { @@ -890,12 +1015,18 @@ "name": "keyword.other.default.scss" }, "constant_functions": { - "begin": "([\\w-]+)(\\()", + "begin": "(?:([\\w-]+)(\\.))?([\\w-]+)(\\()", "beginCaptures": { "1": { - "name": "support.function.misc.scss" + "name": "variable.scss" }, "2": { + "name": "punctuation.access.module.scss" + }, + "3": { + "name": "support.function.misc.scss" + }, + "4": { "name": "punctuation.section.function.scss" } }, @@ -1050,10 +1181,10 @@ "name": "variable.interpolation.scss", "patterns": [ { - "include": "#property_values" + "include": "#variable" }, { - "include": "#variable" + "include": "#property_values" } ] }, @@ -1118,10 +1249,10 @@ "include": "#map" }, { - "include": "#property_values" + "include": "#variable" }, { - "include": "#variable" + "include": "#property_values" } ] }, @@ -1326,7 +1457,7 @@ ] }, "selector_attribute": { - "match": "(?xi)\n(\\[)\n\\s*\n(\n (?:\n [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+?\n)\n(?:\n \\s*([~|^$*]?=)\\s*\n (?:\n (\n (?:\n [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+\n )\n |\n ((\")(.*?)(\"))\n |\n ((')(.*?)('))\n )\n)?\n\\s*\n(\\])", + "match": "(?xi)\n(\\[)\n\\s*\n(\n (?:\n [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\.?\\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+?\n)\n(?:\n \\s*([~|^$*]?=)\\s*\n (?:\n (\n (?:\n [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\.?\\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+\n )\n |\n ((\")(.*?)(\"))\n |\n ((')(.*?)('))\n )\n)?\n\\s*\n(\\])", "name": "meta.attribute-selector.scss", "captures": { "1": { @@ -1421,7 +1552,7 @@ } }, "selector_class": { - "match": "(?x)\n(\\.) # Valid class-name\n(\n (?: [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+\n) # Followed by either:\n(?= $ # - End of the line\n | [\\s,.\\#)\\[:{>+~|] # - Another selector\n | /\\* # - A block comment\n | ; # - A semicolon\n)", + "match": "(?x)\n(\\.) # Valid class-name\n(\n (?: [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\.?\\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+\n) # Followed by either:\n(?= $ # - End of the line\n | [\\s,\\#)\\[:{>+~|] # - Another selector\n | \\.[^$] # - Class selector, negating module variable\n | /\\* # - A block comment\n | ; # - A semicolon\n)", "name": "entity.other.attribute-name.class.css", "captures": { "1": { @@ -1449,7 +1580,7 @@ "name": "entity.name.tag.custom.scss" }, "selector_id": { - "match": "(?x)\n(\\#) # Valid id-name\n(\n (?: [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+\n) # Followed by either:\n(?= $ # - End of the line\n | [\\s,.\\#)\\[:{>+~|] # - Another selector\n | /\\* # - A block comment\n)", + "match": "(?x)\n(\\#) # Valid id-name\n(\n (?: [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\.?\\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+\n) # Followed by either:\n(?= $ # - End of the line\n | [\\s,\\#)\\[:{>+~|] # - Another selector\n | \\.[^$] # - Class selector, negating module variable\n | /\\* # - A block comment\n)", "name": "entity.other.attribute-name.id.css", "captures": { "1": { @@ -1473,7 +1604,7 @@ } }, "selector_placeholder": { - "match": "(?x)\n(%) # Valid placeholder-name\n(\n (?: [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+\n) # Followed by either:\n(?= ; # - End of statement\n | $ # - End of the line\n | [\\s,.\\#)\\[:{>+~|] # - Another selector\n | /\\* # - A block comment\n)", + "match": "(?x)\n(%) # Valid placeholder-name\n(\n (?: [-a-zA-Z_0-9]|[^\\x00-\\x7F] # Valid identifier characters\n | \\\\(?:[0-9a-fA-F]{1,6}|.) # Escape sequence\n | \\#\\{ # Interpolation (escaped to avoid Coffeelint errors)\n | \\.\\$ # Possible start of interpolation module scope variable\n | \\$ # Possible start of interpolation variable\n | } # Possible end of interpolation\n )+\n) # Followed by either:\n(?= ; # - End of statement\n | $ # - End of the line\n | [\\s,\\#)\\[:{>+~|] # - Another selector\n | \\.[^$] # - Class selector, negating module variable\n | /\\* # - A block comment\n)", "name": "entity.other.attribute-name.placeholder.css", "captures": { "1": { @@ -1723,8 +1854,26 @@ ] }, "variables": { - "match": "(\\$|\\-\\-)[A-Za-z0-9_-]+\\b", - "name": "variable.scss" + "patterns": [ + { + "match": "\\b([\\w-]+)(\\.)(\\$[\\w-]+)\\b", + "captures": { + "1": { + "name": "variable.scss" + }, + "2": { + "name": "punctuation.access.module.scss" + }, + "3": { + "name": "variable.scss" + } + } + }, + { + "match": "(\\$|\\-\\-)[A-Za-z0-9_-]+\\b", + "name": "variable.scss" + } + ] } } } \ No newline at end of file diff --git a/extensions/scss/test/colorize-results/test-cssvariables_scss.json b/extensions/scss/test/colorize-results/test-cssvariables_scss.json index e51a05d345fb1..c2862cddcb8ec 100644 --- a/extensions/scss/test/colorize-results/test-cssvariables_scss.json +++ b/extensions/scss/test/colorize-results/test-cssvariables_scss.json @@ -92,9 +92,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -103,9 +103,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -180,9 +180,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -400,9 +400,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -499,9 +499,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -510,9 +510,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -549,4 +549,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/scss/test/colorize-results/test_scss.json b/extensions/scss/test/colorize-results/test_scss.json index 2515405079572..5841c3b8e3f58 100644 --- a/extensions/scss/test/colorize-results/test_scss.json +++ b/extensions/scss/test/colorize-results/test_scss.json @@ -290,9 +290,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -301,9 +301,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -433,9 +433,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -444,9 +444,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -708,9 +708,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -719,9 +719,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1335,9 +1335,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1346,9 +1346,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1368,9 +1368,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1379,9 +1379,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1500,9 +1500,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1511,9 +1511,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -1874,9 +1874,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1962,9 +1962,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1973,9 +1973,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -2182,9 +2182,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2303,9 +2303,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2886,9 +2886,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2897,9 +2897,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -3106,9 +3106,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3117,9 +3117,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -3249,9 +3249,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3260,9 +3260,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -3326,9 +3326,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3337,9 +3337,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -3909,9 +3909,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3942,9 +3942,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3975,9 +3975,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4008,9 +4008,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4184,9 +4184,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4195,9 +4195,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -4239,9 +4239,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4250,9 +4250,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -4305,9 +4305,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4624,9 +4624,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4635,9 +4635,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -4679,9 +4679,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4690,9 +4690,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -4811,9 +4811,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss string.quoted.double.scss variable.interpolation.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4855,9 +4855,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss string.quoted.double.scss variable.interpolation.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -4976,9 +4976,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -5009,9 +5009,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -5020,9 +5020,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -5053,9 +5053,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -5064,9 +5064,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -5196,9 +5196,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -5262,9 +5262,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -5273,9 +5273,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -5339,9 +5339,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -5350,9 +5350,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -5460,9 +5460,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -5471,9 +5471,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -5526,9 +5526,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -5537,9 +5537,9 @@ "t": "source.css.scss meta.definition.variable.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -5845,9 +5845,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.return.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -6043,9 +6043,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -6769,9 +6769,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -6780,9 +6780,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -7022,9 +7022,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -7033,9 +7033,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -7209,9 +7209,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -7220,9 +7220,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -7528,9 +7528,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -7539,9 +7539,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -7825,9 +7825,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -7836,9 +7836,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -8507,9 +8507,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -8518,9 +8518,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -8892,9 +8892,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -8903,9 +8903,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -9343,9 +9343,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.if.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -9387,9 +9387,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.if.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -9431,9 +9431,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.if.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -9508,9 +9508,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -9519,9 +9519,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -9629,9 +9629,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.if.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -9673,9 +9673,9 @@ "t": "source.css.scss meta.property-list.scss meta.at-rule.if.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -9750,9 +9750,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -9761,9 +9761,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -9926,9 +9926,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -9937,9 +9937,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -10564,9 +10564,9 @@ "t": "source.css.scss meta.at-rule.for.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -10608,9 +10608,9 @@ "t": "source.css.scss meta.at-rule.for.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -10773,9 +10773,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -10784,9 +10784,9 @@ "t": "source.css.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -11367,9 +11367,9 @@ "t": "source.css.scss meta.at-rule.each.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -11466,9 +11466,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -11631,9 +11631,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -11642,9 +11642,9 @@ "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 constant.numeric.css keyword.other.unit.em.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -11818,9 +11818,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -12104,9 +12104,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.for.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -12456,9 +12456,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.at-rule.if.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -12544,9 +12544,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -12555,9 +12555,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.percentage.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -13039,9 +13039,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -13050,9 +13050,9 @@ "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 constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -13402,9 +13402,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -13413,9 +13413,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -13589,9 +13589,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.mixin.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -13600,9 +13600,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.at-rule.mixin.scss constant.numeric.css keyword.other.unit.in.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -14535,9 +14535,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -14546,9 +14546,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -14568,9 +14568,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -14579,9 +14579,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -14601,9 +14601,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -14612,9 +14612,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -14667,9 +14667,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -14678,9 +14678,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -14700,9 +14700,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -14711,9 +14711,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -14733,9 +14733,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -14744,9 +14744,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.include.scss constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -16559,9 +16559,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -16570,9 +16570,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.cm.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -16636,9 +16636,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -16647,9 +16647,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-value.scss constant.numeric.css keyword.other.unit.cm.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -16878,9 +16878,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -16889,9 +16889,9 @@ "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 constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -17483,9 +17483,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.at-rule.extend.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -17604,9 +17604,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -18066,9 +18066,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -18077,9 +18077,9 @@ "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 constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -18187,9 +18187,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -18198,9 +18198,9 @@ "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 constant.numeric.css keyword.other.unit.px.css", "r": { "dark_plus": "keyword.other.unit: #B5CEA8", - "light_plus": "keyword.other.unit: #09885A", + "light_plus": "keyword.other.unit: #098658", "dark_vs": "keyword.other.unit: #B5CEA8", - "light_vs": "keyword.other.unit: #09885A", + "light_vs": "keyword.other.unit: #098658", "hc_black": "keyword.other.unit: #B5CEA8" } }, @@ -18880,9 +18880,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -18979,9 +18979,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -19111,9 +19111,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -19221,9 +19221,9 @@ "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 constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -19419,9 +19419,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -19551,9 +19551,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -19738,9 +19738,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -19870,9 +19870,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -20057,9 +20057,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -20167,9 +20167,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.property-list.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -20376,9 +20376,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -20508,9 +20508,9 @@ "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss meta.at-rule.keyframes.scss meta.property-list.scss meta.property-value.scss constant.numeric.css", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -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" diff --git a/extensions/search-result/.vscodeignore b/extensions/search-result/.vscodeignore new file mode 100644 index 0000000000000..da3d276368687 --- /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 new file mode 100644 index 0000000000000..c3e1b53525c99 --- /dev/null +++ b/extensions/search-result/README.md @@ -0,0 +1,5 @@ +# Language Features for Search Result files + +**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. diff --git a/extensions/search-result/extension-browser.webpack.config.js b/extensions/search-result/extension-browser.webpack.config.js new file mode 100644 index 0000000000000..10c0a19e356c8 --- /dev/null +++ b/extensions/search-result/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: __dirname, + entry: { + extension: './src/extension.ts' + }, + output: { + filename: 'extension.js', + path: path.join(__dirname, 'dist') + } +}); diff --git a/extensions/search-result/extension.webpack.config.js b/extensions/search-result/extension.webpack.config.js new file mode 100644 index 0000000000000..de88398eca0d3 --- /dev/null +++ b/extensions/search-result/extension.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 withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + resolve: { + mainFields: ['module', 'main'] + }, + entry: { + extension: './src/extension.ts', + } +}); diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json new file mode 100644 index 0000000000000..7b43da243cf81 --- /dev/null +++ b/extensions/search-result/package.json @@ -0,0 +1,48 @@ +{ + "name": "search-result", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.0", + "publisher": "vscode", + "license": "MIT", + "engines": { + "vscode": "^1.39.0" + }, + "categories": [ + "Programming Languages" + ], + "main": "./out/extension.js", + "browser": "./dist/extension.js", + "activationEvents": [ + "*" + ], + "scripts": { + "generate-grammar": "node ./syntaxes/generateTMLanguage.js", + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:search-result ./tsconfig.json" + }, + "contributes": { + "configurationDefaults": { + "[search-result]": { + "editor.lineNumbers": "off" + } + }, + "languages": [ + { + "id": "search-result", + "extensions": [ + ".code-search" + ], + "aliases": [ + "Search Result" + ] + } + ], + "grammars": [ + { + "language": "search-result", + "scopeName": "text.searchResult", + "path": "./syntaxes/searchResult.tmLanguage.json" + } + ] + } +} diff --git a/extensions/search-result/package.nls.json b/extensions/search-result/package.nls.json new file mode 100644 index 0000000000000..324fd97bcd297 --- /dev/null +++ b/extensions/search-result/package.nls.json @@ -0,0 +1,4 @@ +{ + "displayName": "Search Result", + "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 new file mode 100644 index 0000000000000..3abaa97de5692 --- /dev/null +++ b/extensions/search-result/src/extension.ts @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------------------------- + * 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 pathUtils from 'path'; + +const FILE_LINE_REGEX = /^(\S.*):$/; +const RESULT_LINE_REGEX = /^(\s+)(\d+)(:| )(\s+)(.*)$/; +const SEARCH_RESULT_SELECTOR = { language: 'search-result' }; +const DIRECTIVES = ['# Query:', '# Flags:', '# Including:', '# Excluding:', '# ContextLines:']; +const FLAGS = ['RegExp', 'CaseSensitive', 'IgnoreExcludeSettings', 'WordMatch']; + +let cachedLastParse: { version: number, parse: ParsedSearchResults, uri: vscode.Uri } | undefined; +let documentChangeListener: vscode.Disposable | undefined; + + +export function activate(context: vscode.ExtensionContext) { + + const contextLineDecorations = vscode.window.createTextEditorDecorationType({ opacity: '0.7' }); + const matchLineDecorations = vscode.window.createTextEditorDecorationType({ fontWeight: 'bold' }); + + const decorate = (editor: vscode.TextEditor) => { + const parsed = parseSearchResults(editor.document).filter(isResultLine); + const contextRanges = parsed.filter(line => line.isContext).map(line => line.prefixRange); + const matchRanges = parsed.filter(line => !line.isContext).map(line => line.prefixRange); + editor.setDecorations(contextLineDecorations, contextRanges); + editor.setDecorations(matchLineDecorations, matchRanges); + }; + + if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.languageId === 'search-result') { + decorate(vscode.window.activeTextEditor); + } + + context.subscriptions.push( + + vscode.languages.registerDocumentSymbolProvider(SEARCH_RESULT_SELECTOR, { + provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.DocumentSymbol[] { + const results = parseSearchResults(document, token) + .filter(isFileLine) + .map(line => new vscode.DocumentSymbol( + line.path, + '', + vscode.SymbolKind.File, + line.allLocations.map(({ originSelectionRange }) => originSelectionRange!).reduce((p, c) => p.union(c), line.location.originSelectionRange!), + line.location.originSelectionRange!, + )); + + return results; + } + }), + + vscode.languages.registerCompletionItemProvider(SEARCH_RESULT_SELECTOR, { + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { + + const line = document.lineAt(position.line); + if (position.line > 3) { return []; } + if (position.character === 0 || (position.character === 1 && line.text === '#')) { + const header = Array.from({ length: DIRECTIVES.length }).map((_, i) => document.lineAt(i).text); + + return DIRECTIVES + .filter(suggestion => header.every(line => line.indexOf(suggestion) === -1)) + .map(flag => ({ label: flag, insertText: (flag.slice(position.character)) + ' ' })); + } + + if (line.text.indexOf('# Flags:') === -1) { return []; } + + return FLAGS + .filter(flag => line.text.indexOf(flag) === -1) + .map(flag => ({ label: flag, insertText: flag + ' ' })); + } + }, '#'), + + vscode.languages.registerDefinitionProvider(SEARCH_RESULT_SELECTOR, { + provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.DefinitionLink[] { + const lineResult = parseSearchResults(document, token)[position.line]; + if (!lineResult) { return []; } + if (lineResult.type === 'file') { + return lineResult.allLocations; + } + + const translateRangeSidewaysBy = (r: vscode.Range, n: number) => + r.with({ start: new vscode.Position(r.start.line, Math.max(0, n - r.start.character)), end: new vscode.Position(r.end.line, Math.max(0, n - r.end.character)) }); + + return [{ + ...lineResult.location, + targetSelectionRange: translateRangeSidewaysBy(lineResult.location.targetSelectionRange!, position.character - 1) + }]; + } + }), + + vscode.languages.registerDocumentLinkProvider(SEARCH_RESULT_SELECTOR, { + async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + return parseSearchResults(document, token) + .filter(({ type }) => type === 'file') + .map(({ location }) => ({ range: location.originSelectionRange!, target: location.targetUri })); + } + }), + + vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor?.document.languageId === 'search-result') { + // Clear the parse whenever we open a new editor. + // Conservative because things like the URI might remain constant even if the contents change, and re-parsing even large files is relatively fast. + cachedLastParse = undefined; + + documentChangeListener?.dispose(); + documentChangeListener = vscode.workspace.onDidChangeTextDocument(doc => { + if (doc.document.uri === editor.document.uri) { + decorate(editor); + } + }); + + decorate(editor); + } + }), + + { dispose() { cachedLastParse = undefined; documentChangeListener?.dispose(); } } + ); +} + + +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))); + } + + const uriFromFolderWithPath = (folder: vscode.WorkspaceFolder, path: string): vscode.Uri => + folder.uri.with({ path: pathUtils.join(folder.uri.fsPath, path) }); + + if (vscode.workspace.workspaceFolders) { + const multiRootFormattedPath = /^(.*) • (.*)$/.exec(path); + if (multiRootFormattedPath) { + const [, workspaceName, workspacePath] = multiRootFormattedPath; + const folder = vscode.workspace.workspaceFolders.filter(wf => wf.name === workspaceName)[0]; + if (folder) { + return uriFromFolderWithPath(folder, workspacePath); + } + } + else if (vscode.workspace.workspaceFolders.length === 1) { + 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 uriFromFolderWithPath(prefixMatch, path); + } + } + } + + console.error(`Unable to resolve path ${path}`); + return undefined; +} + +type ParsedSearchFileLine = { type: 'file', location: vscode.LocationLink, allLocations: vscode.LocationLink[], path: string }; +type ParsedSearchResultLine = { type: 'result', location: vscode.LocationLink, isContext: boolean, prefixRange: vscode.Range }; +type ParsedSearchResults = Array; +const isFileLine = (line: ParsedSearchResultLine | ParsedSearchFileLine): line is ParsedSearchFileLine => line.type === 'file'; +const isResultLine = (line: ParsedSearchResultLine | ParsedSearchFileLine): line is ParsedSearchResultLine => line.type === 'result'; + + +function parseSearchResults(document: vscode.TextDocument, token?: vscode.CancellationToken): ParsedSearchResults { + + if (cachedLastParse && cachedLastParse.uri === document.uri && cachedLastParse.version === document.version) { + return cachedLastParse.parse; + } + + const lines = document.getText().split(/\r?\n/); + const links: ParsedSearchResults = []; + + let currentTarget: vscode.Uri | undefined = undefined; + let currentTargetLocations: vscode.LocationLink[] | undefined = undefined; + + for (let i = 0; i < lines.length; i++) { + // TODO: This is probably always false, given we're pegging the thread... + if (token?.isCancellationRequested) { return []; } + const line = lines[i]; + + const fileLine = FILE_LINE_REGEX.exec(line); + if (fileLine) { + const [, path] = fileLine; + + currentTarget = relativePathToUri(path, document.uri); + if (!currentTarget) { continue; } + currentTargetLocations = []; + + const location: vscode.LocationLink = { + targetRange: new vscode.Range(0, 0, 0, 1), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, 0, i, line.length), + }; + + + links[i] = { type: 'file', location, allLocations: currentTargetLocations, path }; + } + + if (!currentTarget) { continue; } + + const resultLine = RESULT_LINE_REGEX.exec(line); + if (resultLine) { + const [, indentation, _lineNumber, seperator, resultIndentation] = resultLine; + const lineNumber = +_lineNumber - 1; + const resultStart = (indentation + _lineNumber + seperator + resultIndentation).length; + const metadataOffset = (indentation + _lineNumber + seperator).length; + + const location: vscode.LocationLink = { + targetRange: new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length), + targetSelectionRange: new vscode.Range(lineNumber, metadataOffset, lineNumber, metadataOffset), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, resultStart, i, line.length), + }; + + currentTargetLocations?.push(location); + + links[i] = { type: 'result', location, isContext: seperator === ' ', prefixRange: new vscode.Range(i, 0, i, metadataOffset) }; + } + } + + cachedLastParse = { + version: document.version, + parse: links, + uri: document.uri + }; + + return links; +} diff --git a/extensions/git/resources/icons/dark/refresh.svg b/extensions/search-result/src/media/refresh-dark.svg similarity index 100% rename from extensions/git/resources/icons/dark/refresh.svg rename to extensions/search-result/src/media/refresh-dark.svg diff --git a/extensions/git/resources/icons/light/refresh.svg b/extensions/search-result/src/media/refresh-light.svg similarity index 100% rename from extensions/git/resources/icons/light/refresh.svg rename to extensions/search-result/src/media/refresh-light.svg diff --git a/extensions/search-result/src/typings/refs.d.ts b/extensions/search-result/src/typings/refs.d.ts new file mode 100644 index 0000000000000..c9849d48e083f --- /dev/null +++ b/extensions/search-result/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. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js new file mode 100644 index 0000000000000..fb74d3696ef9e --- /dev/null +++ b/extensions/search-result/syntaxes/generateTMLanguage.js @@ -0,0 +1,240 @@ +// @ts-check + +const mappings = [ + ['bat', 'source.batchfile'], + ['c', 'source.c'], + ['clj', 'source.clojure'], + ['coffee', 'source.coffee'], + ['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|containerfile|Containerfile)'], + ['fs', 'source.fsharp'], + ['go', 'source.go'], + ['groovy', 'source.groovy'], + ['h', 'source.objc'], + ['handlebars', 'text.html.handlebars', '\\.(?:handlebars|hbs)'], + ['hlsl', 'source.hlsl'], + ['hpp', 'source.objcpp'], + ['html', 'text.html.basic'], + ['ini', 'source.ini'], + ['java', 'source.java'], + ['js', 'source.js'], + ['json', 'source.json.comments'], + ['jsx', 'source.js.jsx'], + ['less', 'source.css.less'], + ['log', 'text.log'], + ['lua', 'source.lua'], + ['m', 'source.objc'], + ['makefile', 'source.makefile', '(?:makefile|Makefile)(?:\\..*)?'], + ['md', 'text.html.markdown'], + ['mm', 'source.objcpp'], + ['p6', 'source.perl.6'], + ['perl', 'source.perl', '\\.(?:perl|pl|pm)'], + ['php', 'source.php'], + ['ps1', 'source.powershell'], + ['pug', 'text.pug'], + ['py', 'source.python'], + ['r', 'source.r'], + ['rb', 'source.ruby'], + ['rs', 'source.rust'], + ['scala', 'source.scala'], + ['scss', 'source.css.scss'], + ['sh', 'source.shell'], + ['sql', 'source.sql'], + ['swift', 'source.swift'], + ['ts', 'source.ts'], + ['tsx', 'source.tsx'], + ['vb', 'source.asp.vb.net'], + ['xml', 'text.xml'], + ['yaml', 'source.yaml', '\\.(?:ya?ml)'], +]; + +const scopes = { + root: 'text.searchResult', + header: { + meta: 'meta.header.search keyword.operator.word.search', + key: 'entity.other.attribute-name', + value: 'entity.other.attribute-value string.unquoted', + flags: { + keyword: 'keyword.other', + }, + contextLines: { + number: 'constant.numeric.integer', + invalid: 'invalid.illegal', + }, + query: { + escape: 'constant.character.escape', + invalid: 'invalid.illegal', + } + }, + resultBlock: { + meta: 'meta.resultBlock.search', + path: { + meta: 'string meta.path.search', + dirname: 'meta.path.dirname.search', + basename: 'meta.path.basename.search', + colon: 'punctuation.separator', + }, + result: { + meta: 'meta.resultLine.search', + metaSingleLine: 'meta.resultLine.singleLine.search', + metaMultiLine: 'meta.resultLine.multiLine.search', + prefix: { + meta: 'constant.numeric.integer meta.resultLinePrefix.search', + metaContext: 'meta.resultLinePrefix.contextLinePrefix.search', + metaMatch: 'meta.resultLinePrefix.matchLinePrefix.search', + lineNumber: 'meta.resultLinePrefix.lineNumber.search', + colon: 'punctuation.separator', + } + } + } +}; + +const repository = {}; +mappings.forEach(([ext, scope, regexp]) => + repository[ext] = { + name: scopes.resultBlock.meta, + begin: `^(?!\\s)(.*?)([^\\\\\\/\\n]*${regexp || `\\.${ext}`})(:)$`, + 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 }, + }, + patterns: [ + { + name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaMultiLine].join(' '), + 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 }, + }, + 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 }, + + '4': { name: scopes.resultBlock.result.prefix.metaContext }, + '5': { name: scopes.resultBlock.result.prefix.lineNumber }, + }, + patterns: [{ include: scope }] + }, + { + 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 }, + }, + patterns: [{ include: scope }] + } + ] + }); + +const header = [ + { + begin: '^(# Query): ', + end: '\n', + name: scopes.header.meta, + beginCaptures: { '1': { name: scopes.header.key }, }, + patterns: [ + { + match: '(\\\\n)|(\\\\\\\\)', + name: [scopes.header.value, scopes.header.query.escape].join(' ') + }, + { + match: '\\\\.|\\\\$', + name: [scopes.header.value, scopes.header.query.invalid].join(' ') + }, + { + match: '[^\\\\\\\n]+', + name: [scopes.header.value].join(' ') + }, + ] + }, + { + begin: '^(# Flags): ', + end: '\n', + name: scopes.header.meta, + beginCaptures: { '1': { name: scopes.header.key }, }, + patterns: [ + { + match: '(RegExp|CaseSensitive|IgnoreExcludeSettings|WordMatch)', + name: [scopes.header.value, 'keyword.other'].join(' ') + }, + { match: '.' }, + ] + }, + { + begin: '^(# ContextLines): ', + end: '\n', + name: scopes.header.meta, + beginCaptures: { '1': { name: scopes.header.key }, }, + patterns: [ + { + match: '\\d', + name: [scopes.header.value, scopes.header.contextLines.number].join(' ') + }, + { match: '.', name: scopes.header.contextLines.invalid }, + ] + }, + { + match: '^(# (?:Including|Excluding)): (.*)$', + name: scopes.header.meta, + captures: { + '1': { name: scopes.header.key }, + '2': { name: scopes.header.value } + } + }, +]; + +const plainText = [ + { + 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 } + } + }, + { + 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 }, + + '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', + scopeName: scopes.root, + patterns: [ + ...header, + ...mappings.map(([ext]) => ({ include: `#${ext}` })), + ...plainText + ], + repository +}; + +require('fs').writeFileSync( + require('path').join(__dirname, './searchResult.tmLanguage.json'), + JSON.stringify(tmLanguage, null, 2)); diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json new file mode 100644 index 0000000000000..e2687fe8a725d --- /dev/null +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -0,0 +1,4570 @@ +{ + "information_for_contributors": "This file is generated from ./generateTMLanguage.js.", + "name": "Search Results", + "scopeName": "text.searchResult", + "patterns": [ + { + "begin": "^(# Query): ", + "end": "\n", + "name": "meta.header.search keyword.operator.word.search", + "beginCaptures": { + "1": { + "name": "entity.other.attribute-name" + } + }, + "patterns": [ + { + "match": "(\\\\n)|(\\\\\\\\)", + "name": "entity.other.attribute-value string.unquoted constant.character.escape" + }, + { + "match": "\\\\.|\\\\$", + "name": "entity.other.attribute-value string.unquoted invalid.illegal" + }, + { + "match": "[^\\\\\\\n]+", + "name": "entity.other.attribute-value string.unquoted" + } + ] + }, + { + "begin": "^(# Flags): ", + "end": "\n", + "name": "meta.header.search keyword.operator.word.search", + "beginCaptures": { + "1": { + "name": "entity.other.attribute-name" + } + }, + "patterns": [ + { + "match": "(RegExp|CaseSensitive|IgnoreExcludeSettings|WordMatch)", + "name": "entity.other.attribute-value string.unquoted keyword.other" + }, + { + "match": "." + } + ] + }, + { + "begin": "^(# ContextLines): ", + "end": "\n", + "name": "meta.header.search keyword.operator.word.search", + "beginCaptures": { + "1": { + "name": "entity.other.attribute-name" + } + }, + "patterns": [ + { + "match": "\\d", + "name": "entity.other.attribute-value string.unquoted constant.numeric.integer" + }, + { + "match": ".", + "name": "invalid.illegal" + } + ] + }, + { + "match": "^(# (?:Including|Excluding)): (.*)$", + "name": "meta.header.search keyword.operator.word.search", + "captures": { + "1": { + "name": "entity.other.attribute-name" + }, + "2": { + "name": "entity.other.attribute-value string.unquoted" + } + } + }, + { + "include": "#bat" + }, + { + "include": "#c" + }, + { + "include": "#clj" + }, + { + "include": "#coffee" + }, + { + "include": "#cpp" + }, + { + "include": "#cs" + }, + { + "include": "#cshtml" + }, + { + "include": "#css" + }, + { + "include": "#dart" + }, + { + "include": "#diff" + }, + { + "include": "#dockerfile" + }, + { + "include": "#fs" + }, + { + "include": "#go" + }, + { + "include": "#groovy" + }, + { + "include": "#h" + }, + { + "include": "#handlebars" + }, + { + "include": "#hlsl" + }, + { + "include": "#hpp" + }, + { + "include": "#html" + }, + { + "include": "#ini" + }, + { + "include": "#java" + }, + { + "include": "#js" + }, + { + "include": "#json" + }, + { + "include": "#jsx" + }, + { + "include": "#less" + }, + { + "include": "#log" + }, + { + "include": "#lua" + }, + { + "include": "#m" + }, + { + "include": "#makefile" + }, + { + "include": "#md" + }, + { + "include": "#mm" + }, + { + "include": "#p6" + }, + { + "include": "#perl" + }, + { + "include": "#php" + }, + { + "include": "#ps1" + }, + { + "include": "#pug" + }, + { + "include": "#py" + }, + { + "include": "#r" + }, + { + "include": "#rb" + }, + { + "include": "#rs" + }, + { + "include": "#scala" + }, + { + "include": "#scss" + }, + { + "include": "#sh" + }, + { + "include": "#sql" + }, + { + "include": "#swift" + }, + { + "include": "#ts" + }, + { + "include": "#tsx" + }, + { + "include": "#vb" + }, + { + "include": "#xml" + }, + { + "include": "#yaml" + }, + { + "match": "^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$", + "name": "meta.resultBlock.search string meta.path.search", + "captures": { + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + } + }, + { + "match": "^ (?:\\s*)(?:((\\d+)(:))|((\\d+)( ))(.*))", + "name": "meta.resultBlock.search meta.resultLine.search", + "captures": { + "1": { + "name": "constant.numeric.integer meta.resultLinePrefix.search meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "constant.numeric.integer meta.resultLinePrefix.search meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + } + } + ], + "repository": { + "bat": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.bat)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.batchfile" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.batchfile" + } + ] + } + ] + }, + "c": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.c)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.c" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.c" + } + ] + } + ] + }, + "clj": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.clj)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.clojure" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.clojure" + } + ] + } + ] + }, + "coffee": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.coffee)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.coffee" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.coffee" + } + ] + } + ] + }, + "cpp": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.(?:cpp|c\\+\\+|cc|cxx|hxx|h\\+\\+|hh))(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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": "^ (?:\\s*)((\\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" + } + ] + } + ] + }, + "cs": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.cs)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.cs" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.cs" + } + ] + } + ] + }, + "cshtml": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.cshtml)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.cshtml" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.cshtml" + } + ] + } + ] + }, + "css": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.css)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.css" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.css" + } + ] + } + ] + }, + "dart": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.dart)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.dart" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.dart" + } + ] + } + ] + }, + "diff": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.diff)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.diff" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.diff" + } + ] + } + ] + }, + "dockerfile": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*(?:dockerfile|Dockerfile|containerfile|Containerfile))(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.dockerfile" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.dockerfile" + } + ] + } + ] + }, + "fs": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.fs)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.fsharp" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.fsharp" + } + ] + } + ] + }, + "go": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.go)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.go" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.go" + } + ] + } + ] + }, + "groovy": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.groovy)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.groovy" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.groovy" + } + ] + } + ] + }, + "h": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.h)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.objc" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.objc" + } + ] + } + ] + }, + "handlebars": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.(?:handlebars|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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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": "^ (?:\\s*)((\\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" + } + ] + } + ] + }, + "hlsl": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.hlsl)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.hlsl" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.hlsl" + } + ] + } + ] + }, + "hpp": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.hpp)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.objcpp" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.objcpp" + } + ] + } + ] + }, + "html": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.html)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.basic" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.basic" + } + ] + } + ] + }, + "ini": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ini)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.ini" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.ini" + } + ] + } + ] + }, + "java": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.java)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.java" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.java" + } + ] + } + ] + }, + "js": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.js)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.js" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.js" + } + ] + } + ] + }, + "json": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.json)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.json.comments" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.json.comments" + } + ] + } + ] + }, + "jsx": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.jsx)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.js.jsx" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.js.jsx" + } + ] + } + ] + }, + "less": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.less)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.css.less" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.css.less" + } + ] + } + ] + }, + "log": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.log)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.log" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.log" + } + ] + } + ] + }, + "lua": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.lua)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.lua" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.lua" + } + ] + } + ] + }, + "m": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.m)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.objc" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.objc" + } + ] + } + ] + }, + "makefile": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*(?:makefile|Makefile)(?:\\..*)?)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.makefile" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.makefile" + } + ] + } + ] + }, + "md": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.md)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.markdown" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.markdown" + } + ] + } + ] + }, + "mm": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.mm)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.objcpp" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.objcpp" + } + ] + } + ] + }, + "p6": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.p6)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.6" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.6" + } + ] + } + ] + }, + "perl": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.(?:perl|pl|pm))(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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": "^ (?:\\s*)((\\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" + } + ] + } + ] + }, + "php": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.php)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.php" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.php" + } + ] + } + ] + }, + "ps1": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ps1)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.powershell" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.powershell" + } + ] + } + ] + }, + "pug": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.pug)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.pug" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.pug" + } + ] + } + ] + }, + "py": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.py)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.python" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.python" + } + ] + } + ] + }, + "r": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.r)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.r" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.r" + } + ] + } + ] + }, + "rb": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.rb)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.ruby" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.ruby" + } + ] + } + ] + }, + "rs": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.rs)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.rust" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.rust" + } + ] + } + ] + }, + "scala": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.scala)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.scala" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.scala" + } + ] + } + ] + }, + "scss": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.scss)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.css.scss" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.css.scss" + } + ] + } + ] + }, + "sh": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.sh)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.shell" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.shell" + } + ] + } + ] + }, + "sql": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.sql)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.sql" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.sql" + } + ] + } + ] + }, + "swift": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.swift)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.swift" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.swift" + } + ] + } + ] + }, + "ts": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ts)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.ts" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.ts" + } + ] + } + ] + }, + "tsx": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.tsx)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.tsx" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.tsx" + } + ] + } + ] + }, + "vb": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.vb)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.asp.vb.net" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.asp.vb.net" + } + ] + } + ] + }, + "xml": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.xml)(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.xml" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.xml" + } + ] + } + ] + }, + "yaml": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.(?:ya?ml))(:)$", + "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": "^ (?:\\s*)((\\d+) )", + "while": "^ (?:\\s*)(?:((\\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.yaml" + } + ] + }, + { + "begin": "^ (?:\\s*)((\\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.yaml" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/extensions/search-result/tsconfig.json b/extensions/search-result/tsconfig.json new file mode 100644 index 0000000000000..16ed233f6e8f6 --- /dev/null +++ b/extensions/search-result/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out", + }, + "include": [ + "src/**/*" + ] +} diff --git a/extensions/search-result/yarn.lock b/extensions/search-result/yarn.lock new file mode 100644 index 0000000000000..fb57ccd13afbd --- /dev/null +++ b/extensions/search-result/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + diff --git a/extensions/shaderlab/language-configuration.json b/extensions/shaderlab/language-configuration.json index 8d91e91a89e40..6af8237672646 100644 --- a/extensions/shaderlab/language-configuration.json +++ b/extensions/shaderlab/language-configuration.json @@ -12,7 +12,7 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + { "open": "\"", "close": "\"", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -20,4 +20,4 @@ ["(", ")"], ["\"", "\""] ] -} \ No newline at end of file +} diff --git a/extensions/shaderlab/test/colorize-results/test_shader.json b/extensions/shaderlab/test/colorize-results/test_shader.json index 7f731af089fd2..fac0a925a41d5 100644 --- a/extensions/shaderlab/test/colorize-results/test_shader.json +++ b/extensions/shaderlab/test/colorize-results/test_shader.json @@ -452,12 +452,12 @@ }, { "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: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, diff --git a/extensions/shared.tsconfig.json b/extensions/shared.tsconfig.json index bb42dd479cfad..a84d7a1cc03d1 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 2fba65c92bd12..ab6a40c6b80e9 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'); @@ -49,7 +50,7 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { loader: 'ts-loader', options: { compilerOptions: { - "sourceMap": true, + 'sourceMap': true, } } }] @@ -63,12 +64,12 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) { // packaging depends on that and this must always be like it filename: '[name].js', path: path.join(extConfig.context, 'dist'), - libraryTarget: "commonjs", + libraryTarget: 'commonjs', }, // 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/language-configuration.json b/extensions/shellscript/language-configuration.json index 964623ac4cc15..8421a3817a275 100644 --- a/extensions/shellscript/language-configuration.json +++ b/extensions/shellscript/language-configuration.json @@ -11,9 +11,9 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["`", "`"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] }, + { "open": "`", "close": "`", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json index 211401a070c23..50c3ffdb799fa 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 7abcea36e5deb..45e7c7f4e7b89 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "Microsoft/vscode-mssql", "repositoryUrl": "https://github.com/Microsoft/vscode-mssql", - "commitHash": "a79741f76fd33bd137a8c28172c9750b978309b6" + "commitHash": "61ae0eb21ac53883a23e09913a5ae77a59126ff9" } }, "license": "MIT", - "version": "1.6.0" + "version": "1.9.0" } ], "version": 1 diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 1dd6903783f61..de4dc029f8064 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.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-mssql/commit/a79741f76fd33bd137a8c28172c9750b978309b6", + "version": "https://github.com/Microsoft/vscode-mssql/commit/61ae0eb21ac53883a23e09913a5ae77a59126ff9", "name": "SQL", "scopeName": "source.sql", "patterns": [ diff --git a/extensions/sql/test/colorize-results/test_sql.json b/extensions/sql/test/colorize-results/test_sql.json index da116cea9c3f2..4bc7ff281520e 100644 --- a/extensions/sql/test/colorize-results/test_sql.json +++ b/extensions/sql/test/colorize-results/test_sql.json @@ -136,9 +136,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -180,9 +180,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -213,9 +213,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -268,9 +268,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -290,9 +290,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -318,4 +318,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/swift/language-configuration.json b/extensions/swift/language-configuration.json index 2bdc27527498e..54095ef5212e2 100644 --- a/extensions/swift/language-configuration.json +++ b/extensions/swift/language-configuration.json @@ -12,9 +12,9 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["`", "`"] + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] }, + { "open": "`", "close": "`", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -24,4 +24,4 @@ ["'", "'"], ["`", "`"] ] -} \ No newline at end of file +} diff --git a/extensions/swift/package.json b/extensions/swift/package.json index f88ac07ea5e22..a8f75268efce4 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 2d67b7a13e10e..91a374d4716c9 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/swift/test/colorize-results/test_swift.json b/extensions/swift/test/colorize-results/test_swift.json index 8512da234c9df..977818bdec72a 100644 --- a/extensions/swift/test/colorize-results/test_swift.json +++ b/extensions/swift/test/colorize-results/test_swift.json @@ -48,9 +48,9 @@ "t": "source.swift constant.numeric.integer.decimal.swift", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -802,4 +802,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index f0a326bb34d9b..7afe3bd963ee0 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -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,16 +256,22 @@ } }, { - "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" } } ], "colors": { + "editor.background": "#000c18", + "editor.foreground": "#6688cc", + // Base // "foreground": "", "focusBorder": "#596F99", @@ -295,8 +315,6 @@ "scrollbarSlider.hoverBackground": "#3B3F5188", // Editor - "editor.background": "#000c18", - // "editor.foreground": "#6688cc", "editorWidget.background": "#262641", "editorCursor.foreground": "#ddbb88", "editorWhitespace.foreground": "#103050", @@ -433,5 +451,6 @@ "terminal.ansiBrightMagenta": "#d778ff", "terminal.ansiBrightCyan": "#78ffff", "terminal.ansiBrightWhite": "#ffffff" - } + }, + "semanticHighlighting": true } diff --git a/extensions/theme-defaults/themes/dark_defaults.json b/extensions/theme-defaults/themes/dark_defaults.json index 00c2ac8c36b0b..5d20a90c62d43 100644 --- a/extensions/theme-defaults/themes/dark_defaults.json +++ b/extensions/theme-defaults/themes/dark_defaults.json @@ -17,6 +17,10 @@ "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 f8a0ff5b7c515..4fd89793214e3 100644 --- a/extensions/theme-defaults/themes/dark_plus.json +++ b/extensions/theme-defaults/themes/dark_plus.json @@ -9,7 +9,8 @@ "entity.name.function", "support.function", "support.constant.handlebars", - "source.powershell variable.other.member" + "source.powershell variable.other.member", + "entity.name.operator.custom-literal" // See https://en.cppreference.com/w/cpp/language/user_literal ], "settings": { "foreground": "#DCDCAA" @@ -96,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": [ @@ -179,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 479b5fd8988d8..1b4cf8b967ee2 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" @@ -171,7 +170,8 @@ }, { "scope": [ - "meta.preprocessor" + "meta.preprocessor", + "entity.name.function.preprocessor" ], "settings": { "foreground": "#569cd6" @@ -225,7 +225,7 @@ { "scope": [ "string", - "entity.name.operator.custom-literal.string", + "meta.embedded.assembly" ], "settings": { "foreground": "#ce9178" @@ -362,5 +362,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 f76d7bb960f26..436dfa52912a9 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -114,6 +114,19 @@ "settings": { "foreground": "#CE9178" } + }, + { + "name": "HC Search Editor context line override", + "scope": "meta.resultLinePrefix.contextLinePrefix.search", + "settings": { + "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 9d11138a99b7b..495a15238dca6 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -136,6 +136,7 @@ { "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#6796e6" } }, @@ -337,5 +338,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 e28c9b8ed0b2f..cbf573c0e9d44 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": "#81818130", + "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 c7599d60d57a8..b743b1b998a49 100644 --- a/extensions/theme-defaults/themes/light_plus.json +++ b/extensions/theme-defaults/themes/light_plus.json @@ -9,7 +9,8 @@ "entity.name.function", "support.function", "support.constant.handlebars", - "source.powershell variable.other.member" + "source.powershell variable.other.member", + "entity.name.operator.custom-literal" // See https://en.cppreference.com/w/cpp/language/user_literal ], "settings": { "foreground": "#795E26" @@ -96,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": [ @@ -158,7 +169,7 @@ "keyword.control.anchor.regexp" ], "settings": { - "foreground": "#ff0000" + "foreground": "#EE0000" } }, { @@ -170,7 +181,7 @@ { "scope": "constant.character.escape", "settings": { - "foreground": "#ff0000" + "foreground": "#EE0000" } }, { @@ -179,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 886d1175c8b6f..3410551898b0f 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -45,13 +45,12 @@ { "scope": [ "constant.numeric", - "entity.name.operator.custom-literal.number", "variable.other.enummember", "keyword.operator.plus.exponent", "keyword.operator.minus.exponent" ], "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -130,7 +129,7 @@ { "scope": "markup.inserted", "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -169,7 +168,8 @@ }, { "scope": [ - "meta.preprocessor" + "meta.preprocessor", + "entity.name.function.preprocessor" ], "settings": { "foreground": "#0000ff" @@ -184,7 +184,7 @@ { "scope": "meta.preprocessor.numeric", "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -217,7 +217,7 @@ { "scope": [ "string", - "entity.name.operator.custom-literal.string", + "meta.embedded.assembly" ], "settings": { "foreground": "#a31515" @@ -344,7 +344,7 @@ { "scope": "keyword.other.unit", "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -365,7 +365,7 @@ { "scope": "constant.sha.git-rebase", "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -386,5 +386,11 @@ "foreground": "#0000ff" } } - ] + ], + "semanticTokenColors": { + "newOperator": "#0000ff", + "stringLiteral": "#a31515", + "customLiteral": "#000000", + "numberLiteral": "#098658", + } } 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 111a4a23d9bc1..38c8fe099687c 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -260,7 +260,7 @@ "entity.name.section" ], "settings": { - "fontStyle": "", + "fontStyle": "bold", "foreground": "#8ab1b0" } }, @@ -394,5 +394,6 @@ "foreground": "#dc3958" } } - ] + ], + "semanticHighlighting": true } 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 f0b6126d5fdec..5140f5ad3d096 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -42,7 +42,23 @@ "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/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 6bc2e84ce98c1..a30508946574f 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -382,7 +382,66 @@ "name": "Markup Setext Header", "scope": "markup.heading.setext", "settings": { - "fontStyle": "", + "foreground": "#A6E22E", + "fontStyle": "bold" + } + }, + { + "name": "Markup Headings", + "scope": "markup.heading.markdown", + "settings": { + "fontStyle": "bold" + } + }, + { + "name": "Markdown Quote", + "scope": "markup.quote.markdown", + "settings": { + "fontStyle": "italic", + "foreground": "#75715E" + } + }, + { + "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": "#E6DB74" + } + }, + { + "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": "#f8f8f2" + } + }, + { + "name": "Markdown List Punctuation", + "scope": [ + "punctuation.definition.list.begin.markdown" + ], + "settings": { "foreground": "#A6E22E" } }, @@ -417,5 +476,6 @@ "foreground": "#FD971F" } } - ] + ], + "semanticHighlighting": true } diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index ae19ba7889b75..ffcb30cff03f1 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -494,5 +494,6 @@ "walkThrough.embeddedEditorBackground": "#00000014", "editorIndentGuide.background": "#aaaaaa60", "editorIndentGuide.activeBackground": "#777777b0" - } + }, + "semanticHighlighting": true } diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index 277e7a8db3f19..dbe8011320930 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -192,7 +192,7 @@ }, { "name": "Support.constant", - "scope": "support.constant", + "scope": [ "support.constant", "support.variable"], "settings": { "fontStyle": "", "foreground": "#eb939aff" @@ -351,25 +351,37 @@ } }, { - "name": "Markup Inline", - "scope": "markup.inline.raw", + "name": "Markup: Strong", + "scope": "markup.bold", "settings": { - "fontStyle": "", - "foreground": "#cd8d8dff" + "fontStyle": "bold" } }, { - "name": "Markup Headings", - "scope": "markup.heading", + "name": "Markup: Emphasis", + "scope": "markup.italic", "settings": { - "foreground": "#fec758ff" + "fontStyle": "italic" } }, { - "name": "Markup Setext Header", - "scope": "markup.heading.setext", + "name": "Markup Inline", + "scope": "markup.inline.raw", "settings": { "fontStyle": "", + "foreground": "#cd8d8dff" + } + }, + { + "name": "Headings", + "scope": [ + "markup.heading", + "markup.heading.setext", + "punctuation.definition.heading", + "entity.name.section" + ], + "settings": { + "fontStyle": "bold", "foreground": "#fec758ff" } }, @@ -385,5 +397,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 3e14951e34228..85228d704bf0b 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 1c86b8bcb2bc5..8899dae032beb 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": "719e5d384e878b0e190abc80247a8726f083a393" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index b85727d01e463..e1a5a63449703 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 825ac52ee830c..29712ae6b919a 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/719e5d384e878b0e190abc80247a8726f083a393" } \ No newline at end of file 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 682444485d50b..eaf90258d3546 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -270,6 +270,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 +296,7 @@ "name": "Markup Headings", "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#268BD2" } }, @@ -477,5 +492,6 @@ "terminal.ansiBrightMagenta": "#6c71c4", "terminal.ansiBrightCyan": "#93a1a1", "terminal.ansiBrightWhite": "#fdf6e3" - } + }, + "semanticHighlighting": true } 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 a29c8fb32f07f..77aa0f2907934 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": {} }, { @@ -270,6 +273,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 +299,7 @@ "name": "Markup Headings", "scope": "markup.heading", "settings": { + "fontStyle": "bold", "foreground": "#268BD2" } }, @@ -484,5 +502,6 @@ // Interactive Playground "walkThrough.embeddedEditorBackground": "#00000014" - } + }, + "semanticHighlighting": true } diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json index f8c47a29e7b86..bdccdb49d9161 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-theme.json @@ -223,6 +223,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 +245,13 @@ "foreground": "#FF9DA4" } }, + { + "name": "Markup Headings", + "scope": "markup.heading", + "settings": { + "fontStyle": "bold" + } + }, { "scope": "token.info-token", "settings": { @@ -255,5 +276,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 0000000000000..99a5b2271d630 --- /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 0000000000000..5dbe0fdc5053c --- /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 06c7b11c5e6c5..0a0a50bc3e054 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 527fa2c1d43e4..a42b323ed1409 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,12 +48,12 @@ function patchJsdoctype(grammar) { } function patchGrammar(grammar) { - return removeDom(patchJsdoctype(grammar)); + return removeNodeTypes(removeDom(patchJsdoctype(grammar))); } function adaptToJavaScript(grammar, replacementScope) { grammar.name = 'JavaScript (with React support)'; - grammar.fileTypes = ['.js', '.jsx', '.es6', '.mjs']; + grammar.fileTypes = ['.js', '.jsx', '.es6', '.mjs', '.cjs']; grammar.scopeName = `source${replacementScope}`; var fixScopeNames = function (rule) { diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index 6323be5b029a2..623873b2ce4d4 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "TypeScript-TmLanguage", "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", - "commitHash": "477c1b17e273b64af13040c064c9ed62c8b32fba" + "commitHash": "fa4e0d3a918db0eab8e5c5be952f3bd649968456" } }, "license": "MIT", diff --git a/extensions/typescript-basics/package.json b/extensions/typescript-basics/package.json index 6fb73d06a9d05..e3120789cf6f9 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,32 +79,76 @@ "meta.import string.quoted": "other", "variable.other.jsdoc": "other" } - }, + } + ], + "semanticTokenScopes": [ { - "scopeName": "documentation.injection.ts", - "path": "./syntaxes/jsdoc.ts.injection.tmLanguage.json", - "injectTo": [ - "source.ts", - "source.tsx" - ] + "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" + ] + } }, { - "scopeName": "documentation.injection.js.jsx", - "path": "./syntaxes/jsdoc.js.injection.tmLanguage.json", - "injectTo": [ - "source.js", - "source.js.jsx" - ] + "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.code-snippets b/extensions/typescript-basics/snippets/typescript.code-snippets new file mode 100644 index 0000000000000..8eeb13e2e2d58 --- /dev/null +++ b/extensions/typescript-basics/snippets/typescript.code-snippets @@ -0,0 +1,309 @@ +{ + "Constructor": { + "prefix": "ctor", + "body": [ + "/**", + " *", + " */", + "constructor() {", + "\tsuper();", + "\t$0", + "}" + ], + "description": "Constructor" + }, + "Class Definition": { + "prefix": "class", + "body": [ + "class ${1:name} {", + "\tconstructor(${2:parameters}) {", + "\t\t$0", + "\t}", + "}" + ], + "description": "Class Definition" + }, + "Public Method Definition": { + "prefix": "public method", + "body": [ + "/**", + " * ${1:name}", + " */", + "public ${1:name}() {", + "\t$0", + "}" + ], + "description": "Public Method Definition" + }, + "Private Method Definition": { + "prefix": "private method", + "body": [ + "private ${1:name}() {", + "\t$0", + "}" + ], + "description": "Private Method Definition" + }, + "Import external module.": { + "prefix": "import statement", + "body": [ + "import { $0 } from \"${1:module}\";" + ], + "description": "Import external module." + }, + "Property getter": { + "prefix": "get", + "body": [ + "", + "public get ${1:value}() : ${2:string} {", + "\t${3:return $0}", + "}", + "" + ], + "description": "Property getter" + }, + "Log to the console": { + "prefix": "log", + "body": [ + "console.log($1);", + "$0" + ], + "description": "Log to the console" + }, + "Log warning to console": { + "prefix": "warn", + "body": [ + "console.warn($1);", + "$0" + ], + "description": "Log warning to the console" + }, + "Log error to console": { + "prefix": "error", + "body": [ + "console.error($1);", + "$0" + ], + "description": "Log error to the console" + }, + "Define a full property": { + "prefix": "prop", + "body": [ + "", + "private _${1:value} : ${2:string};", + "public get ${1:value}() : ${2:string} {", + "\treturn this._${1:value};", + "}", + "public set ${1:value}(v : ${2:string}) {", + "\tthis._${1:value} = v;", + "}", + "" + ], + "description": "Define a full property" + }, + "Triple-slash reference": { + "prefix": "ref", + "body": [ + "/// ", + "$0" + ], + "description": "Triple-slash reference" + }, + "Property setter": { + "prefix": "set", + "body": [ + "", + "public set ${1:value}(v : ${2:string}) {", + "\tthis.$3 = v;", + "}", + "" + ], + "description": "Property setter" + }, + "Throw Exception": { + "prefix": "throw", + "body": [ + "throw new Error(\"$1\");", + "$0" + ], + "description": "Throw Exception" + }, + "For Loop": { + "prefix": "for", + "body": [ + "for (let ${1:index} = 0; ${1:index} < ${2:array}.length; ${1:index}++) {", + "\tconst ${3:element} = ${2:array}[${1:index}];", + "\t$0", + "}" + ], + "description": "For Loop" + }, + "For-Each Loop using =>": { + "prefix": "foreach =>", + "body": [ + "${1:array}.forEach(${2:element} => {", + "\t$0", + "});" + ], + "description": "For-Each Loop using =>" + }, + "For-In Loop": { + "prefix": "forin", + "body": [ + "for (const ${1:key} in ${2:object}) {", + "\tif (Object.prototype.hasOwnProperty.call(${2:object}, ${1:key})) {", + "\t\tconst ${3:element} = ${2:object}[${1:key}];", + "\t\t$0", + "\t}", + "}" + ], + "description": "For-In Loop" + }, + "For-Of Loop": { + "prefix": "forof", + "body": [ + "for (const ${1:iterator} of ${2:object}) {", + "\t$0", + "}" + ], + "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": [ + "function ${1:name}(${2:params}:${3:type}) {", + "\t$0", + "}" + ], + "description": "Function Statement" + }, + "If Statement": { + "prefix": "if", + "body": [ + "if (${1:condition}) {", + "\t$0", + "}" + ], + "description": "If Statement" + }, + "If-Else Statement": { + "prefix": "ifelse", + "body": [ + "if (${1:condition}) {", + "\t$0", + "} else {", + "\t", + "}" + ], + "description": "If-Else Statement" + }, + "New Statement": { + "prefix": "new", + "body": [ + "const ${1:name} = new ${2:type}(${3:arguments});$0" + ], + "description": "New Statement" + }, + "Switch Statement": { + "prefix": "switch", + "body": [ + "switch (${1:key}) {", + "\tcase ${2:value}:", + "\t\t$0", + "\t\tbreak;", + "", + "\tdefault:", + "\t\tbreak;", + "}" + ], + "description": "Switch Statement" + }, + "While Statement": { + "prefix": "while", + "body": [ + "while (${1:condition}) {", + "\t$0", + "}" + ], + "description": "While Statement" + }, + "Do-While Statement": { + "prefix": "dowhile", + "body": [ + "do {", + "\t$0", + "} while (${1:condition});" + ], + "description": "Do-While Statement" + }, + "Try-Catch Statement": { + "prefix": "trycatch", + "body": [ + "try {", + "\t$0", + "} catch (${1:error}) {", + "\t", + "}" + ], + "description": "Try-Catch Statement" + }, + "Set Timeout Function": { + "prefix": "settimeout", + "body": [ + "setTimeout(() => {", + "\t$0", + "}, ${1:timeout});" + ], + "description": "Set Timeout Function" + }, + "Region Start": { + "prefix": "#region", + "body": [ + "//#region $0" + ], + "description": "Folding Region Start" + }, + "Region End": { + "prefix": "#endregion", + "body": [ + "//#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/snippets/typescript.json b/extensions/typescript-basics/snippets/typescript.json deleted file mode 100644 index 0a7195751d821..0000000000000 --- a/extensions/typescript-basics/snippets/typescript.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "Constructor": { - "prefix": "ctor", - "body": [ - "/**", - " *", - " */", - "constructor() {", - "\tsuper();", - "\t$0", - "}" - ], - "description": "Constructor" - }, - "Class Definition": { - "prefix": "class", - "body": [ - "class ${1:name} {", - "\tconstructor(${2:parameters}) {", - "\t\t$0", - "\t}", - "}" - ], - "description": "Class Definition" - }, - "Public Method Definition": { - "prefix": "public method", - "body": [ - "/**", - " * ${1:name}", - " */", - "public ${1:name}() {", - "\t$0", - "}" - ], - "description": "Public Method Definition" - }, - "Private Method Definition": { - "prefix": "private method", - "body": [ - "private ${1:name}() {", - "\t$0", - "}" - ], - "description": "Private Method Definition" - }, - "Import external module.": { - "prefix": "import statement", - "body": [ - "import { $0 } from \"${1:module}\";" - ], - "description": "Import external module." - }, - "Property getter": { - "prefix": "get", - "body": [ - "", - "public get ${1:value}() : ${2:string} {", - "\t${3:return $0}", - "}", - "" - ], - "description": "Property getter" - }, - "Log to the console": { - "prefix": "log", - "body": [ - "console.log($1);", - "$0" - ], - "description": "Log to the console" - }, - "Log warning to console": { - "prefix": "warn", - "body": [ - "console.warn($1);", - "$0" - ], - "description": "Log warning to the console" - }, - "Log error to console": { - "prefix": "error", - "body": [ - "console.error($1);", - "$0" - ], - "description": "Log error to the console" - }, - "Define a full property": { - "prefix": "prop", - "body": [ - "", - "private _${1:value} : ${2:string};", - "public get ${1:value}() : ${2:string} {", - "\treturn this._${1:value};", - "}", - "public set ${1:value}(v : ${2:string}) {", - "\tthis._${1:value} = v;", - "}", - "" - ], - "description": "Define a full property" - }, - "Triple-slash reference": { - "prefix": "ref", - "body": [ - "/// ", - "$0" - ], - "description": "Triple-slash reference" - }, - "Property setter": { - "prefix": "set", - "body": [ - "", - "public set ${1:value}(v : ${2:string}) {", - "\tthis.$3 = v;", - "}", - "" - ], - "description": "Property setter" - }, - "Throw Exception": { - "prefix": "throw", - "body": [ - "throw \"$1\";", - "$0" - ], - "description": "Throw Exception" - }, - "For Loop": { - "prefix": "for", - "body": [ - "for (let ${1:index} = 0; ${1:index} < ${2:array}.length; ${1:index}++) {", - "\tconst ${3:element} = ${2:array}[${1:index}];", - "\t$0", - "}" - ], - "description": "For Loop" - }, - "For-Each Loop using =>": { - "prefix": "foreach =>", - "body": [ - "${1:array}.forEach(${2:element} => {", - "\t$0", - "});" - ], - "description": "For-Each Loop using =>" - }, - "For-In Loop": { - "prefix": "forin", - "body": [ - "for (const ${1:key} in ${2:object}) {", - "\tif (${2:object}.hasOwnProperty(${1:key})) {", - "\t\tconst ${3:element} = ${2:object}[${1:key}];", - "\t\t$0", - "\t}", - "}" - ], - "description": "For-In Loop" - }, - "For-Of Loop": { - "prefix": "forof", - "body": [ - "for (const ${1:iterator} of ${2:object}) {", - "\t$0", - "}" - ], - "description": "For-Of Loop" - }, - "Function Statement": { - "prefix": "function", - "body": [ - "function ${1:name}(${2:params}:${3:type}) {", - "\t$0", - "}" - ], - "description": "Function Statement" - }, - "If Statement": { - "prefix": "if", - "body": [ - "if (${1:condition}) {", - "\t$0", - "}" - ], - "description": "If Statement" - }, - "If-Else Statement": { - "prefix": "ifelse", - "body": [ - "if (${1:condition}) {", - "\t$0", - "} else {", - "\t", - "}" - ], - "description": "If-Else Statement" - }, - "New Statement": { - "prefix": "new", - "body": [ - "const ${1:name} = new ${2:type}(${3:arguments});$0" - ], - "description": "New Statement" - }, - "Switch Statement": { - "prefix": "switch", - "body": [ - "switch (${1:key}) {", - "\tcase ${2:value}:", - "\t\t$0", - "\t\tbreak;", - "", - "\tdefault:", - "\t\tbreak;", - "}" - ], - "description": "Switch Statement" - }, - "While Statement": { - "prefix": "while", - "body": [ - "while (${1:condition}) {", - "\t$0", - "}" - ], - "description": "While Statement" - }, - "Do-While Statement": { - "prefix": "dowhile", - "body": [ - "do {", - "\t$0", - "} while (${1:condition});" - ], - "description": "Do-While Statement" - }, - "Try-Catch Statement": { - "prefix": "trycatch", - "body": [ - "try {", - "\t$0", - "} catch (${1:error}) {", - "\t", - "}" - ], - "description": "Try-Catch Statement" - }, - "Set Timeout Function": { - "prefix": "settimeout", - "body": [ - "setTimeout(() => {", - "\t$0", - "}, ${1:timeout});" - ], - "description": "Set Timeout Function" - }, - "Region Start": { - "prefix": "#region", - "body": [ - "//#region $0" - ], - "description": "Folding Region Start" - }, - "Region End": { - "prefix": "#endregion", - "body": [ - "//#endregion" - ], - "description": "Folding Region End" - } -} diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index ccdd30167e394..10187a61673ee 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.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/TypeScript-TmLanguage/commit/477c1b17e273b64af13040c064c9ed62c8b32fba", + "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,17 +27,9 @@ "name": "punctuation.definition.comment.ts" } } - } - ], - "repository": { + }, "statements": { "patterns": [ - { - "include": "#string" - }, - { - "include": "#comment" - }, { "include": "#declaration" }, @@ -53,6 +50,12 @@ }, { "include": "#punctuation-semicolon" + }, + { + "include": "#string" + }, + { + "include": "#comment" } ] }, @@ -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*(?]|=[^<]|\\<\\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*(?]|=[^<]|\\<\\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*(?]|=[^<]|\\<\\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*(?]|=[^<]|\\<\\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)(?:(?)\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*(?]|=[^<]|\\<\\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)(?:(?)\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*(?]|=[^<]|\\<\\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" @@ -1078,13 +1081,13 @@ }, "field-declaration": { "name": "meta.field.declaration.ts", - "begin": "(?x)(?)\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*(?]|=[^<]|\\<\\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*(?]|=[^<]|\\<\\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" @@ -1120,7 +1123,7 @@ }, { "name": "meta.definition.property.ts variable.object.property.ts", - "match": "[_$[:alpha:]][_$[:alnum:]]*" + "match": "\\#?[_$[:alpha:]][_$[:alnum:]]*" }, { "name": "keyword.operator.optional.ts", @@ -1428,7 +1431,7 @@ }, { "name": "meta.arrow.ts", - "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# 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# 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" @@ -1822,7 +1825,7 @@ }, "access-modifier": { "name": "storage.modifier.ts", - "match": "(?)\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" @@ -2670,7 +2688,7 @@ "name": "keyword.control.as.ts" } }, - "end": "(?=$|^|[,}]|\\|\\||\\&\\&|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?)", + "patterns": [ + { + "include": "#type-parameters" + } + ] + }, + { + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "beginCaptures": { + "1": { + "name": "meta.brace.round.ts" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "meta.brace.round.ts" + } + }, + "patterns": [ + { + "include": "#expression-inside-possibly-arrow-parens" + } + ] + }, { "include": "#possibly-arrow-return-type" }, @@ -2785,26 +2836,75 @@ ] }, "function-call": { - "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { - "name": "meta.function-call.ts", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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": "\\?\\." @@ -2812,12 +2912,6 @@ { "name": "meta.function-call.ts keyword.operator.definiteassignment.ts", "match": "\\!" - }, - { - "include": "#type-arguments" - }, - { - "include": "#paren-expression" } ] }, @@ -2849,7 +2943,7 @@ "name": "keyword.operator.new.ts" } }, - "end": "(?<=\\))|(?=[;),}\\]:\\-\\+]|\\|\\||\\&\\&|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|(([\\&\\~\\^\\|]\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s+instanceof(?![_$[:alnum:]])(?:(?=\\.\\.\\.)|(?!\\.)))|((?)\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*(?]|=[^<]|\\<\\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)(?:(?)\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*(?]|=[^<]|\\<\\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" @@ -2983,7 +3077,7 @@ } }, { - "match": "(?x)(?:(?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?=|<>|<|>" }, + { + "match": "(?<=[_$[:alnum:]])(\\!)\\s*(/)(?![/*])", + "captures": { + "1": { + "name": "keyword.operator.logical.ts" + }, + "2": { + "name": "keyword.operator.arithmetic.ts" + } + } + }, { "name": "keyword.operator.logical.ts", "match": "\\!|&&|\\|\\||\\?\\?" @@ -3476,23 +3581,6 @@ } } }, - { - "match": "(?x)(?)\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" @@ -3631,7 +3691,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3645,7 +3705,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3675,7 +3735,7 @@ "match": "([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\\??\\.\\s*prototype\\b(?!\\$))" }, { - "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n (\\#?[[:upper:]][_$[:digit:][:upper:]]*) |\n (\\#?[_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3692,7 +3752,7 @@ } }, { - "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "variable.other.constant.object.ts" @@ -4011,6 +4071,24 @@ } }, "patterns": [ + { + "name": "keyword.operator.rest.ts", + "match": "\\.\\.\\." + }, + { + "match": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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" @@ -4555,7 +4637,7 @@ }, { "name": "string.template.ts", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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" @@ -4621,7 +4703,7 @@ "patterns": [ { "name": "string.regexp.ts", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.ts" @@ -4644,7 +4726,7 @@ }, { "name": "string.regexp.ts", - "begin": "((?)\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.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*(?\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\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.tsx variable.other.constant.tsx entity.name.function.tsx" @@ -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.tsx" @@ -1081,13 +1084,13 @@ }, "field-declaration": { "name": "meta.field.declaration.tsx", - "begin": "(?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)(\\#?[_$[: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.tsx entity.name.function.tsx" @@ -1123,7 +1126,7 @@ }, { "name": "meta.definition.property.tsx variable.object.property.tsx", - "match": "[_$[:alpha:]][_$[:alnum:]]*" + "match": "\\#?[_$[:alpha:]][_$[:alnum:]]*" }, { "name": "keyword.operator.optional.tsx", @@ -1431,7 +1434,7 @@ }, { "name": "meta.arrow.tsx", - "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.tsx" @@ -1825,7 +1828,7 @@ }, "access-modifier": { "name": "storage.modifier.tsx", - "match": "(?)\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" @@ -2673,7 +2691,7 @@ "name": "keyword.control.as.tsx" } }, - "end": "(?=$|^|[,}]|\\|\\||\\&\\&|((?]|\\|\\||\\&\\&|\\!\\=\\=|$|^|((?)", + "patterns": [ + { + "include": "#type-parameters" + } + ] + }, + { + "begin": "(?<=\\>)\\s*(\\()(?=\\s*((([\\{\\[]\\s*)?$)|((\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})\\s*((:\\s*\\{?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))|((\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])\\s*((:\\s*\\[?$)|((\\s*([^<>\\(\\)\\{\\}]|\\<([^<>]|\\<[^<>]+\\>)+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*)?=\\s*)))))", + "beginCaptures": { + "1": { + "name": "meta.brace.round.tsx" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "meta.brace.round.tsx" + } + }, + "patterns": [ + { + "include": "#expression-inside-possibly-arrow-parens" + } + ] + }, { "include": "#possibly-arrow-return-type" }, @@ -2788,26 +2839,75 @@ ] }, "function-call": { - "begin": "(?=(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", - "end": "(?<=\\))(?!(((([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))|(?<=[\\)]))\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?))*(?)*(?\\s*)?\\()", "patterns": [ { - "name": "meta.function-call.tsx", - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(?:(\\?\\.\\s*)|(\\!))?(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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": "\\?\\." @@ -2815,12 +2915,6 @@ { "name": "meta.function-call.tsx keyword.operator.definiteassignment.tsx", "match": "\\!" - }, - { - "include": "#type-arguments" - }, - { - "include": "#paren-expression" } ] }, @@ -2852,7 +2946,7 @@ "name": "keyword.operator.new.tsx" } }, - "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.tsx" @@ -2986,7 +3080,7 @@ } }, { - "match": "(?x)(?:(?]|\\|\\||\\&\\&|\\!\\=\\=|$|((?=|<>|<|>" }, + { + "match": "(?<=[_$[:alnum:]])(\\!)\\s*(/)(?![/*])", + "captures": { + "1": { + "name": "keyword.operator.logical.tsx" + }, + "2": { + "name": "keyword.operator.arithmetic.tsx" + } + } + }, { "name": "keyword.operator.logical.tsx", "match": "\\!|&&|\\|\\||\\?\\?" @@ -3427,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.tsx" @@ -3582,7 +3642,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[[:upper:]][_$[:digit:][:upper:]]*)(?![_$[:alnum:]])", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3596,7 +3656,7 @@ } }, { - "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*([_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3626,7 +3686,7 @@ "match": "([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\\??\\.\\s*prototype\\b(?!\\$))" }, { - "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*(?:\n (\\#?[[:upper:]][_$[:digit:][:upper:]]*) |\n (\\#?[_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3643,7 +3703,7 @@ } }, { - "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)", + "match": "(?x)(?:\n ([[:upper:]][_$[:digit:][:upper:]]*) |\n ([_$[:alpha:]][_$[:alnum:]]*)\n)(?=\\s*\\??\\.\\s*\\#?[_$[:alpha:]][_$[:alnum:]]*)", "captures": { "1": { "name": "variable.other.constant.object.tsx" @@ -3962,6 +4022,24 @@ } }, "patterns": [ + { + "name": "keyword.operator.rest.tsx", + "match": "\\.\\.\\." + }, + { + "match": "(?\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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" @@ -4506,7 +4588,7 @@ }, { "name": "string.template.tsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))(([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer|typeof)\\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" @@ -4572,7 +4654,7 @@ "patterns": [ { "name": "string.regexp.tsx", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([gimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.tsx" @@ -4595,7 +4677,7 @@ }, { "name": "string.regexp.tsx", - "begin": "((? { + return Terser.minify(content.toString()).code; + + }, + transformPath: (targetPath) => { + return targetPath.replace('tsserver.js', 'tsserver.web.js'); + } + } + ], + }), + ], +}); diff --git a/extensions/typescript-language-features/extension.webpack.config.js b/extensions/typescript-language-features/extension.webpack.config.js index de88398eca0d3..b8f7766676137 100644 --- a/extensions/typescript-language-features/extension.webpack.config.js +++ b/extensions/typescript-language-features/extension.webpack.config.js @@ -14,6 +14,9 @@ module.exports = withDefaults({ resolve: { mainFields: ['module', 'main'] }, + externals: { + 'typescript-vscode-sh-plugin': 'commonjs vscode' // used by build/lib/extensions to know what node_modules to bundle + }, entry: { extension: './src/extension.ts', } diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 605b3443f43b6..70ed27503cd35 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -16,20 +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.14", "vscode-extension-telemetry": "0.1.1", - "vscode-nls": "^4.0.0" + "vscode-nls": "^4.1.1" }, "devDependencies": { "@types/node": "^12.11.7", - "@types/semver": "^5.5.0", "@types/rimraf": "2.0.2", - "vscode": "^1.1.10" + "@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", @@ -45,9 +51,11 @@ "onCommand:typescript.openTsServerLog", "onCommand:workbench.action.tasks.runTask", "onCommand:_typescript.configurePlugin", + "onCommand:_typescript.learnMoreAboutRefactorings", "onLanguage:jsonc" ], "main": "./out/extension", + "browser": "./dist/browser/extension", "contributes": { "jsonValidation": [ { @@ -56,7 +64,7 @@ }, { "fileMatch": "tsconfig.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig.json", @@ -64,7 +72,7 @@ }, { "fileMatch": "tsconfig.*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig-*.json", @@ -72,7 +80,7 @@ }, { "fileMatch": "tsconfig-*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json" + "url": "https://json.schemastore.org/tsconfig" }, { "fileMatch": "tsconfig.*.json", @@ -80,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", @@ -108,7 +116,7 @@ }, { "fileMatch": "jsconfig.*.json", - "url": "https://schemastore.azurewebsites.net/schemas/json/jsconfig.json" + "url": "https://json.schemastore.org/jsconfig" }, { "fileMatch": "jsconfig.*.json", @@ -138,6 +146,12 @@ "usesOnlineServices" ] }, + "typescript.enablePromptUseWorkspaceTsdk": { + "type": "boolean", + "default": false, + "description": "%typescript.enablePromptUseWorkspaceTsdk%", + "scope": "window" + }, "typescript.npm": { "type": [ "string", @@ -159,12 +173,24 @@ "description": "%javascript.referencesCodeLens.enabled%", "scope": "window" }, + "javascript.referencesCodeLens.showOnAllFunctions": { + "type": "boolean", + "default": false, + "description": "%javascript.referencesCodeLens.showOnAllFunctions%", + "scope": "window" + }, "typescript.referencesCodeLens.enabled": { "type": "boolean", "default": false, "description": "%typescript.referencesCodeLens.enabled%", "scope": "window" }, + "typescript.referencesCodeLens.showOnAllFunctions": { + "type": "boolean", + "default": false, + "description": "%typescript.referencesCodeLens.showOnAllFunctions%", + "scope": "window" + }, "typescript.implementationsCodeLens.enabled": { "type": "boolean", "default": false, @@ -624,16 +650,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": { @@ -707,6 +799,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" } } }, @@ -859,51 +1030,84 @@ "background": { "activeOnStart": true, "beginsPattern": { - "regexp": "^\\s*(?:message TS6032:|\\[?\\d{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM| a\\.m\\.| p\\.m\\.)?(?:\\]| -)) 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{1,2}:\\d{1,2}:\\d{1,2}(?: AM| PM| a\\.m\\.| p\\.m\\.)?(?:\\]| -)) (?: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\\." } } } ], "codeActions": [ { - "kind": "refactor.extract.constant", - "title": "%codeActions.refactor.extract.constant%", - "selector": [ + "languages": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], + "actions": [ { - "language": "javascript" + "kind": "refactor.extract.constant", + "title": "%codeActions.refactor.extract.constant.title%", + "description": "%codeActions.refactor.extract.constant.description%" }, { - "language": "javascriptreact" + "kind": "refactor.extract.function", + "title": "%codeActions.refactor.extract.function.title%", + "description": "%codeActions.refactor.extract.function.description%" }, { - "language": "typescript" + "kind": "refactor.extract.interface", + "title": "%codeActions.refactor.extract.interface.title%", + "description": "%codeActions.refactor.extract.interface.description%" }, { - "language": "typescriptreact" - } - ] - }, - { - "kind": "source.organizeImports", - "title": "%codeActions.source.organizeImports%", - "selector": [ + "kind": "refactor.extract.type", + "title": "%codeActions.refactor.extract.type.title%", + "description": "%codeActions.refactor.extract.type.description%" + }, { - "language": "javascript" + "kind": "refactor.rewrite.import", + "title": "%codeActions.refactor.rewrite.import.title%", + "description": "%codeActions.refactor.rewrite.import.description%" }, { - "language": "javascriptreact" + "kind": "refactor.rewrite.export", + "title": "%codeActions.refactor.rewrite.export.title%", + "description": "%codeActions.refactor.rewrite.export.description%" }, { - "language": "typescript" + "kind": "refactor.rewrite.arrow.braces", + "title": "%codeActions.refactor.rewrite.arrow.braces.title%", + "description": "%codeActions.refactor.rewrite.arrow.braces.description%" }, { - "language": "typescriptreact" + "kind": "refactor.rewrite.parameters.toDestructured", + "title": "%codeActions.refactor.rewrite.parameters.toDestructured.title%" + }, + { + "kind": "refactor.rewrite.property.generateAccessors", + "title": "%codeActions.refactor.rewrite.property.generateAccessors.title%", + "description": "%codeActions.refactor.rewrite.property.generateAccessors.description%" + }, + { + "kind": "refactor.move.newFile", + "title": "%codeActions.refactor.move.newFile.title%", + "description": "%codeActions.refactor.move.newFile.description%" + }, + { + "kind": "source.organizeImports", + "title": "%codeActions.source.organizeImports.title%" } ] } + ], + "typescriptServerPlugins": [ + { + "name": "typescript-vscode-sh-plugin", + "enableForWorkspaceTypeScriptVersions": true + } ] } } diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index bf8014744f4c8..6009e3671229f 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,28 +24,30 @@ "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.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.", - "format.semicolons.ignore": "Don’t insert or remove any semicolons.", + "format.semicolons.ignore": "Don't insert or remove any semicolons.", "format.semicolons.insert": "Insert semicolons at statement ends.", "format.semicolons.remove": "Remove unnecessary semicolons.", "javascript.validate.enable": "Enable/disable JavaScript validation.", "goToProjectConfig.title": "Go to Project Configuration", "javascript.referencesCodeLens.enabled": "Enable/disable references CodeLens in JavaScript files.", + "javascript.referencesCodeLens.showOnAllFunctions": "Enable/disable references CodeLens on all functions in JavaScript files.", "typescript.referencesCodeLens.enabled": "Enable/disable references CodeLens in TypeScript files.", + "typescript.referencesCodeLens.showOnAllFunctions": "Enable/disable references CodeLens on all functions in TypeScript files.", "typescript.implementationsCodeLens.enabled": "Enable/disable implementations CodeLens. This CodeLens shows the implementers of an interface.", "typescript.openTsServerLog.title": "Open TS Server log", "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.", - "typescript.check.npmIsInstalled": "Check if NPM is installed for Automatic Type Acquisition.", + "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.", "typescript.tsc.autoDetect.off": "Disable this feature.", @@ -56,26 +59,74 @@ "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.", - "codeActions.refactor.extract.constant": "Extract constant", - "codeActions.source.organizeImports": "Organize imports" + "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", + "codeActions.refactor.extract.function.description": "Extract expression to method or function.", + "codeActions.refactor.extract.type.title": "Extract type", + "codeActions.refactor.extract.type.description": "Extract type to a type alias.", + "codeActions.refactor.extract.interface.title": "Extract interface", + "codeActions.refactor.extract.interface.description": "Extract type to an interface.", + "codeActions.refactor.rewrite.import.title": "Convert import", + "codeActions.refactor.rewrite.import.description": "Convert between named imports and namespace imports.", + "codeActions.refactor.rewrite.export.title": "Convert export", + "codeActions.refactor.rewrite.export.description": "Convert between default export and named export.", + "codeActions.refactor.move.newFile.title": "Move to a new file", + "codeActions.refactor.move.newFile.description": "Move the expression to a new file.", + "codeActions.refactor.rewrite.arrow.braces.title": "Rewrite arrow braces", + "codeActions.refactor.rewrite.arrow.braces.description": "Add or remove braces in an arrow function.", + "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" } diff --git a/extensions/typescript-language-features/schemas/jsconfig.schema.json b/extensions/typescript-language-features/schemas/jsconfig.schema.json index ddee9fc57af4e..8724f9b3aee37 100644 --- a/extensions/typescript-language-features/schemas/jsconfig.schema.json +++ b/extensions/typescript-language-features/schemas/jsconfig.schema.json @@ -1,4 +1,5 @@ { + "allowTrailingCommas": true, "title": "JSON schema for the JavaScript configuration file", "type": "object", "default": { diff --git a/extensions/typescript-language-features/schemas/package.schema.json b/extensions/typescript-language-features/schemas/package.schema.json index c26af63c93b94..c135ea39ec12a 100644 --- a/extensions/typescript-language-features/schemas/package.schema.json +++ b/extensions/typescript-language-features/schemas/package.schema.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "title": "TypeScript contributions to package.json", "type": "object", "properties": { @@ -28,4 +28,4 @@ } } } -} \ No newline at end of file +} 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 8af85d8b94edd..f781c4a50fa82 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 dbc10ea2e2698..11adec3525178 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,78 +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 { - 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 | undefined; - try { - res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); - } catch { - // noop - } - if (!res || res.type !== 'response' || !res.body) { - vscode.window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project')); - return; - } - - const { configFileName } = res.body; - if (configFileName && !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( - (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 f22b3e448de50..7ab7ae09d202b 100644 --- a/extensions/typescript-language-features/src/commands/index.ts +++ b/extensions/typescript-language-features/src/commands/index.ts @@ -4,21 +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'; -export function registerCommands( +export function registerBaseCommands( commandManager: CommandManager, lazyClientHost: Lazy, pluginManager: PluginManager -) { +): void { commandManager.register(new ReloadTypeScriptProjectsCommand(lazyClientHost)); commandManager.register(new ReloadJavaScriptProjectsCommand(lazyClientHost)); commandManager.register(new SelectTypeScriptVersionCommand(lazyClientHost)); @@ -27,4 +28,5 @@ export function registerCommands( commandManager.register(new TypeScriptGoToProjectConfigCommand(lazyClientHost)); commandManager.register(new JavaScriptGoToProjectConfigCommand(lazyClientHost)); commandManager.register(new ConfigurePluginCommand(pluginManager)); -} \ No newline at end of file + commandManager.register(new LearnMoreAboutRefactoringsCommand()); +} diff --git a/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts b/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.ts new file mode 100644 index 0000000000000..b166397e38b14 --- /dev/null +++ b/extensions/typescript-language-features/src/commands/learnMoreAboutRefactorings.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 * as vscode from 'vscode'; +import { isTypeScriptDocument } from '../utils/languageModeIds'; +import { Command } from './commandManager'; + +export class LearnMoreAboutRefactoringsCommand implements Command { + public static readonly id = '_typescript.learnMoreAboutRefactorings'; + public readonly id = LearnMoreAboutRefactoringsCommand.id; + + public execute() { + 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 be47a985cd7b3..cd41445fef201 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 fbba68eb94e3a..4da59685f673c 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 a357224d35244..77dcae870ee9c 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 dc771df8bc2db..d70f59472ffc1 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 0000000000000..6a0f97241ea1a --- /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(); + 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.v400)); + + 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 ffa61f231f1be..1d6ccd2914d31 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,104 +30,37 @@ export function activate( const onCompletionAccepted = new vscode.EventEmitter(); context.subscriptions.push(onCompletionAccepted); - const lazyClientHost = createLazyClientHost(context, pluginManager, commandManager, item => { - onCompletionAccepted.fire(item); - }); + const logDirectoryProvider = new NodeLogDirectoryProvider(context); + const versionProvider = new DiskTypeScriptVersionProvider(); - registerCommands(commandManager, lazyClientHost, pluginManager); - context.subscriptions.push(vscode.workspace.registerTaskProvider('typescript', new TscTaskProvider(lazyClientHost.map(x => x.serviceClient)))); context.subscriptions.push(new LanguageConfigurationManager()); - import('./features/tsconfig').then(module => { - context.subscriptions.push(module.register()); + const lazyClientHost = createLazyClientHost(context, onCaseInsenitiveFileSystem(), { + pluginManager, + commandManager, + logDirectoryProvider, + cancellerFactory: nodeRequestCancellerFactory, + versionProvider, + processFactory: ChildServerProcess, + }, item => { + onCompletionAccepted.fire(item); }); - context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager)); - - return getExtensionApi(onCompletionAccepted.event, pluginManager); -} - -function createLazyClientHost( - context: vscode.ExtensionContext, - pluginManager: PluginManager, - commandManager: CommandManager, - onCompletionAccepted: (item: vscode.CompletionItem) => void, -): Lazy { - return lazy(() => { - const logDirectoryProvider = new LogDirectoryProvider(context); - - const clientHost = new TypeScriptServiceClientHost( - standardLanguageDescriptions, - context.workspaceState, - pluginManager, - commandManager, - logDirectoryProvider, - onCompletionAccepted); - - context.subscriptions.push(clientHost); + registerBaseCommands(commandManager, lazyClientHost, pluginManager); - context.subscriptions.push(new Surveyor(context.globalState, clientHost.serviceClient)); - - clientHost.serviceClient.onReady(() => { - context.subscriptions.push( - ProjectStatus.create( - clientHost.serviceClient, - clientHost.serviceClient.telemetryReporter)); - }); - - return clientHost; + import('./task/taskProvider').then(module => { + context.subscriptions.push(module.register(lazyClientHost.map(x => x.serviceClient))); }); -} - -function lazilyActivateClient( - lazyClientHost: Lazy, - 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 - // tslint:disable-next-line:no-unused-expression - 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); - } + import('./languageFeatures/tsconfig').then(module => { + context.subscriptions.push(module.register()); + }); - return vscode.Disposable.from(...disposables); -} + context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager)); -function isSupportedDocument( - supportedLanguage: string[], - document: vscode.TextDocument -): boolean { - if (supportedLanguage.indexOf(document.languageId) < 0) { - return false; - } - return fileSchemes.isSupportedScheme(document.uri.scheme); + return getExtensionApi(onCompletionAccepted.event, pluginManager); } export function deactivate() { - rimraf.sync(electron.getInstanceDir()); + rimraf.sync(temp.getInstanceTempDir()); } diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts deleted file mode 100644 index bcb4ea1b4fef1..0000000000000 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ /dev/null @@ -1,560 +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 API from '../utils/api'; -import { Delayer } from '../utils/async'; -import { nulToken } from '../utils/cancellation'; -import { Disposable } from '../utils/dispose'; -import * as languageModeIds from '../utils/languageModeIds'; -import { ResourceMap } from '../utils/resourceMap'; -import * as typeConverters from '../utils/typeConverters'; - -const enum BufferKind { - TypeScript = 1, - JavaScript = 2, -} - -const enum BufferState { - Initial = 1, - Open = 2, - Closed = 2, -} - -function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined { - switch (mode) { - case languageModeIds.typescript: return 'TS'; - case languageModeIds.typescriptreact: return 'TSX'; - case languageModeIds.javascript: return 'JS'; - case languageModeIds.javascriptreact: return 'JSX'; - } - return undefined; -} - -class CloseOperation { - readonly type = 'close'; - constructor( - public readonly args: string - ) { } -} - -class OpenOperation { - readonly type = 'open'; - constructor( - public readonly args: Proto.OpenRequestArgs - ) { } -} - -class ChangeOperation { - readonly type = 'change'; - constructor( - public readonly args: Proto.FileCodeEdits - ) { } -} - -/** - * Manages synchronization of buffers with the TS server. - * - * If supported, batches together file changes. This allows the TS server to more efficiently process changes. - */ -class BufferSynchronizer { - - private readonly _pending = new ResourceMap(); - - constructor( - private readonly client: ITypeScriptServiceClient - ) { } - - public open(resource: vscode.Uri, args: Proto.OpenRequestArgs) { - if (this.supportsBatching) { - this.updatePending(resource, pending => { - pending.set(resource, new OpenOperation(args)); - }); - } else { - this.client.executeWithoutWaitingForResponse('open', args); - } - } - - public close(resource: vscode.Uri, filepath: string) { - if (this.supportsBatching) { - this.updatePending(resource, pending => { - pending.set(resource, new CloseOperation(filepath)); - }); - } else { - const args: Proto.FileRequestArgs = { file: filepath }; - this.client.executeWithoutWaitingForResponse('close', args); - } - } - - public change(resource: vscode.Uri, filepath: string, events: readonly vscode.TextDocumentContentChangeEvent[]) { - if (!events.length) { - return; - } - - if (this.supportsBatching) { - this.updatePending(resource, pending => { - pending.set(resource, new ChangeOperation({ - fileName: filepath, - textChanges: events.map((change): Proto.CodeEdit => ({ - newText: change.text, - start: typeConverters.Position.toLocation(change.range.start), - end: typeConverters.Position.toLocation(change.range.end), - })).reverse(), // Send the edits end-of-document to start-of-document order - })); - }); - } else { - for (const { range, text } of events) { - const args: Proto.ChangeRequestArgs = { - insertString: text, - ...typeConverters.Range.toFormattingRequestArgs(filepath, range) - }; - this.client.executeWithoutWaitingForResponse('change', args); - } - } - } - - public beforeCommand(command: string) { - if (command === 'updateOpen') { - return; - } - - this.flush(); - } - - private flush() { - if (!this.supportsBatching) { - // We've already eagerly synchronized - this._pending.clear(); - return; - } - - if (this._pending.size > 0) { - const closedFiles: string[] = []; - const openFiles: Proto.OpenRequestArgs[] = []; - const changedFiles: Proto.FileCodeEdits[] = []; - for (const change of this._pending.values) { - switch (change.type) { - case 'change': changedFiles.push(change.args); break; - case 'open': openFiles.push(change.args); break; - case 'close': closedFiles.push(change.args); break; - } - } - this.client.execute('updateOpen', { changedFiles, closedFiles, openFiles }, nulToken, { nonRecoverable: true }); - this._pending.clear(); - } - } - - private get supportsBatching(): boolean { - return this.client.apiVersion.gte(API.v340) && vscode.workspace.getConfiguration('typescript', null).get('useBatchedBufferSync', true); - } - - private updatePending(resource: vscode.Uri, f: (pending: ResourceMap) => void): void { - if (this._pending.has(resource)) { - // we saw this file before, make sure we flush before working with it again - this.flush(); - } - f(this._pending); - } -} - -class SyncedBuffer { - - private state = BufferState.Initial; - - constructor( - private readonly document: vscode.TextDocument, - public readonly filepath: string, - private readonly client: ITypeScriptServiceClient, - private readonly synchronizer: BufferSynchronizer, - ) { } - - public open(): void { - const args: Proto.OpenRequestArgs = { - file: this.filepath, - fileContent: this.document.getText(), - projectRootPath: this.client.getWorkspaceRootForResource(this.document.uri), - }; - - const scriptKind = mode2ScriptKind(this.document.languageId); - if (scriptKind) { - args.scriptKindName = scriptKind; - } - - if (this.client.apiVersion.gte(API.v240)) { - const tsPluginsForDocument = this.client.pluginManager.plugins - .filter(x => x.languages.indexOf(this.document.languageId) >= 0); - - if (tsPluginsForDocument.length) { - (args as any).plugins = tsPluginsForDocument.map(plugin => plugin.name); - } - } - - this.synchronizer.open(this.resource, args); - this.state = BufferState.Open; - } - - public get resource(): vscode.Uri { - return this.document.uri; - } - - public get lineCount(): number { - return this.document.lineCount; - } - - public get kind(): BufferKind { - switch (this.document.languageId) { - case languageModeIds.javascript: - case languageModeIds.javascriptreact: - return BufferKind.JavaScript; - - case languageModeIds.typescript: - case languageModeIds.typescriptreact: - default: - return BufferKind.TypeScript; - } - } - - public close(): void { - this.synchronizer.close(this.resource, this.filepath); - this.state = BufferState.Closed; - } - - public onContentChanged(events: readonly vscode.TextDocumentContentChangeEvent[]): void { - if (this.state !== BufferState.Open) { - console.error(`Unexpected buffer state: ${this.state}`); - } - - this.synchronizer.change(this.resource, this.filepath, events); - } -} - -class SyncedBufferMap extends ResourceMap { - - public getForPath(filePath: string): SyncedBuffer | undefined { - return this.get(vscode.Uri.file(filePath)); - } - - public get allBuffers(): Iterable { - return this.values; - } -} - -class PendingDiagnostics extends ResourceMap { - public getOrderedFileSet(): ResourceMap { - const orderedResources = Array.from(this.entries) - .sort((a, b) => a.value - b.value) - .map(entry => entry.resource); - - const map = new ResourceMap(); - for (const resource of orderedResources) { - map.set(resource, undefined); - } - return map; - } -} - -class GetErrRequest { - - public static executeGetErrRequest( - client: ITypeScriptServiceClient, - files: ResourceMap, - onDone: () => void - ) { - const token = new vscode.CancellationTokenSource(); - return new GetErrRequest(client, files, token, onDone); - } - - private _done: boolean = false; - - private constructor( - client: ITypeScriptServiceClient, - public readonly files: ResourceMap, - private readonly _token: vscode.CancellationTokenSource, - onDone: () => void - ) { - const args: Proto.GeterrRequestArgs = { - delay: 0, - files: Array.from(files.entries) - .map(entry => client.normalizedPath(entry.resource)) - .filter(x => !!x) as string[] - }; - - client.executeAsync('geterr', args, _token.token) - .finally(() => { - if (this._done) { - return; - } - this._done = true; - onDone(); - }); - } - - public cancel(): any { - if (!this._done) { - this._token.cancel(); - } - - this._token.dispose(); - } -} - -export default class BufferSyncSupport extends Disposable { - - private readonly client: ITypeScriptServiceClient; - - private _validateJavaScript: boolean = true; - private _validateTypeScript: boolean = true; - private readonly modeIds: Set; - private readonly syncedBuffers: SyncedBufferMap; - private readonly pendingDiagnostics: PendingDiagnostics; - private readonly diagnosticDelayer: Delayer; - private pendingGetErr: GetErrRequest | undefined; - private listening: boolean = false; - private readonly synchronizer: BufferSynchronizer; - - constructor( - client: ITypeScriptServiceClient, - modeIds: string[] - ) { - super(); - this.client = client; - this.modeIds = new Set(modeIds); - - this.diagnosticDelayer = new Delayer(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.updateConfiguration(); - vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); - } - - private readonly _onDelete = this._register(new vscode.EventEmitter()); - public readonly onDelete = this._onDelete.event; - - public listen(): void { - if (this.listening) { - return; - } - this.listening = true; - vscode.workspace.onDidOpenTextDocument(this.openTextDocument, this, this._disposables); - vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this._disposables); - vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this._disposables); - vscode.window.onDidChangeVisibleTextEditors(e => { - for (const { document } of e) { - const syncedBuffer = this.syncedBuffers.get(document.uri); - if (syncedBuffer) { - this.requestDiagnostic(syncedBuffer); - } - } - }, this, this._disposables); - vscode.workspace.textDocuments.forEach(this.openTextDocument, this); - } - - public handles(resource: vscode.Uri): boolean { - return this.syncedBuffers.has(resource); - } - - public ensureHasBuffer(resource: vscode.Uri): boolean { - if (this.syncedBuffers.has(resource)) { - return true; - } - - const existingDocument = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === resource.toString()); - if (existingDocument) { - return this.openTextDocument(existingDocument); - } - - return false; - } - - public toVsCodeResource(resource: vscode.Uri): vscode.Uri { - const filepath = this.client.normalizedPath(resource); - for (const buffer of this.syncedBuffers.allBuffers) { - if (buffer.filepath === filepath) { - return buffer.resource; - } - } - return resource; - } - - public toResource(filePath: string): vscode.Uri { - const buffer = this.syncedBuffers.getForPath(filePath); - if (buffer) { - return buffer.resource; - } - return vscode.Uri.file(filePath); - } - - public reOpenDocuments(): void { - for (const buffer of this.syncedBuffers.allBuffers) { - buffer.open(); - } - } - - public openTextDocument(document: vscode.TextDocument): boolean { - if (!this.modeIds.has(document.languageId)) { - return false; - } - const resource = document.uri; - const filepath = this.client.normalizedPath(resource); - if (!filepath) { - return false; - } - - if (this.syncedBuffers.has(resource)) { - return true; - } - - const syncedBuffer = new SyncedBuffer(document, filepath, this.client, this.synchronizer); - this.syncedBuffers.set(resource, syncedBuffer); - syncedBuffer.open(); - this.requestDiagnostic(syncedBuffer); - return true; - } - - public closeResource(resource: vscode.Uri): void { - const syncedBuffer = this.syncedBuffers.get(resource); - if (!syncedBuffer) { - return; - } - this.pendingDiagnostics.delete(resource); - this.syncedBuffers.delete(resource); - syncedBuffer.close(); - this._onDelete.fire(resource); - this.requestAllDiagnostics(); - } - - public interuptGetErr(f: () => R): R { - if (!this.pendingGetErr) { - return f(); - } - - this.pendingGetErr.cancel(); - this.pendingGetErr = undefined; - const result = f(); - this.triggerDiagnostics(); - return result; - } - - public beforeCommand(command: string): void { - this.synchronizer.beforeCommand(command); - } - - private onDidCloseTextDocument(document: vscode.TextDocument): void { - this.closeResource(document.uri); - } - - private onDidChangeTextDocument(e: vscode.TextDocumentChangeEvent): void { - const syncedBuffer = this.syncedBuffers.get(e.document.uri); - if (!syncedBuffer) { - return; - } - - syncedBuffer.onContentChanged(e.contentChanges); - const didTrigger = this.requestDiagnostic(syncedBuffer); - - if (!didTrigger && this.pendingGetErr) { - // In this case we always want to re-trigger all diagnostics - this.pendingGetErr.cancel(); - this.pendingGetErr = undefined; - this.triggerDiagnostics(); - } - } - - public requestAllDiagnostics() { - for (const buffer of this.syncedBuffers.allBuffers) { - if (this.shouldValidate(buffer)) { - this.pendingDiagnostics.set(buffer.resource, Date.now()); - } - } - this.triggerDiagnostics(); - } - - public getErr(resources: vscode.Uri[]): any { - const handledResources = resources.filter(resource => this.handles(resource)); - if (!handledResources.length) { - return; - } - - for (const resource of handledResources) { - this.pendingDiagnostics.set(resource, Date.now()); - } - - this.triggerDiagnostics(); - } - - private triggerDiagnostics(delay: number = 200) { - this.diagnosticDelayer.trigger(() => { - this.sendPendingDiagnostics(); - }, delay); - } - - private requestDiagnostic(buffer: SyncedBuffer): boolean { - if (!this.shouldValidate(buffer)) { - return false; - } - - this.pendingDiagnostics.set(buffer.resource, Date.now()); - - const delay = Math.min(Math.max(Math.ceil(buffer.lineCount / 20), 300), 800); - this.triggerDiagnostics(delay); - return true; - } - - public hasPendingDiagnostics(resource: vscode.Uri): boolean { - return this.pendingDiagnostics.has(resource); - } - - private sendPendingDiagnostics(): void { - const orderedFileSet = this.pendingDiagnostics.getOrderedFileSet(); - - if (this.pendingGetErr) { - this.pendingGetErr.cancel(); - - for (const file of this.pendingGetErr.files.entries) { - orderedFileSet.set(file.resource, undefined); - } - } - - // Add all open TS buffers to the geterr request. They might be visible - for (const buffer of this.syncedBuffers.values) { - orderedFileSet.set(buffer.resource, undefined); - } - - if (orderedFileSet.size) { - const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, orderedFileSet, () => { - if (this.pendingGetErr === getErr) { - this.pendingGetErr = undefined; - } - }); - } - - this.pendingDiagnostics.clear(); - } - - private updateConfiguration() { - const jsConfig = vscode.workspace.getConfiguration('javascript', null); - const tsConfig = vscode.workspace.getConfiguration('typescript', null); - - this._validateJavaScript = jsConfig.get('validate.enable', true); - this._validateTypeScript = tsConfig.get('validate.enable', true); - } - - private shouldValidate(buffer: SyncedBuffer) { - switch (buffer.kind) { - case BufferKind.JavaScript: - return this._validateJavaScript; - - case BufferKind.TypeScript: - default: - return this._validateTypeScript; - } - } -} diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts deleted file mode 100644 index dde8a7f2f68a1..0000000000000 --- a/extensions/typescript-language-features/src/features/completions.ts +++ /dev/null @@ -1,715 +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 * as PConst from '../protocol.const'; -import { 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 * as Previewer from '../utils/previewer'; -import { snippetForFunctionCall } from '../utils/snippetForFunctionCall'; -import TelemetryReporter from '../utils/telemetry'; -import * as typeConverters from '../utils/typeConverters'; -import TypingsStatus from '../utils/typingsStatus'; -import FileConfigurationManager from './fileConfigurationManager'; - -const localize = nls.loadMessageBundle(); - -interface DotAccessorContext { - readonly range: vscode.Range; - readonly text: string; -} - -interface CompletionContext { - readonly isNewIdentifierLocation: boolean; - readonly isMemberCompletion: boolean; - readonly isInValidCommitCharacterContext: boolean; - readonly enableCallCompletions: boolean; - readonly dotAccessorContext?: DotAccessorContext; -} - -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, - 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 - this.sortText = '\uffff' + tsEntry.sortText; - } else { - this.sortText = tsEntry.sortText; - } - - if (tsEntry.isRecommended) { - this.preselect = true; - } - - 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.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; - - if (completionContext.isMemberCompletion && completionContext.dotAccessorContext) { - this.filterText = completionContext.dotAccessorContext.text + (this.insertText || this.label); - if (!this.range) { - this.range = completionContext.dotAccessorContext.range; - this.insertText = this.filterText; - } - } - - if (tsEntry.kindModifiers) { - const kindModifiers = new Set(tsEntry.kindModifiers.split(/\s+/g)); - if (kindModifiers.has(PConst.KindModifiers.optional)) { - if (!this.insertText) { - this.insertText = this.label; - } - - if (!this.filterText) { - this.filterText = this.label; - } - this.label += '?'; - } - - if (kindModifiers.has(PConst.KindModifiers.color)) { - this.kind = vscode.CompletionItemKind.Color; - } - - if (tsEntry.kind === PConst.Kind.script) { - for (const extModifier of PConst.KindModifiers.fileExtensionKindModifiers) { - if (kindModifiers.has(extModifier)) { - if (tsEntry.name.toLowerCase().endsWith(extModifier)) { - this.detail = tsEntry.name; - } else { - this.detail = tsEntry.name + extModifier; - } - break; - } - } - } - } - this.resolveRange(line); - } - - private resolveRange(line: string): 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; - } - - // 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 static convertKind(kind: string): vscode.CompletionItemKind { - switch (kind) { - case PConst.Kind.primitiveType: - case PConst.Kind.keyword: - return vscode.CompletionItemKind.Keyword; - case PConst.Kind.const: - return vscode.CompletionItemKind.Constant; - case PConst.Kind.let: - case PConst.Kind.variable: - case PConst.Kind.localVariable: - case PConst.Kind.alias: - return vscode.CompletionItemKind.Variable; - case PConst.Kind.memberVariable: - case PConst.Kind.memberGetAccessor: - case PConst.Kind.memberSetAccessor: - return vscode.CompletionItemKind.Field; - case PConst.Kind.function: - return vscode.CompletionItemKind.Function; - case PConst.Kind.memberFunction: - 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.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; - } - return vscode.CompletionItemKind.Property; - } - - @memoize - public get commitCharacters(): string[] | undefined { - if (this.completionContext.isNewIdentifierLocation || !this.completionContext.isInValidCommitCharacterContext) { - return undefined; - } - - const commitCharacters: string[] = []; - switch (this.tsEntry.kind) { - case PConst.Kind.memberGetAccessor: - case PConst.Kind.memberSetAccessor: - case PConst.Kind.constructSignature: - case PConst.Kind.callSignature: - case PConst.Kind.indexSignature: - case PConst.Kind.enum: - case PConst.Kind.interface: - commitCharacters.push('.', ';'); - break; - - case PConst.Kind.module: - case PConst.Kind.alias: - case PConst.Kind.const: - case PConst.Kind.let: - case PConst.Kind.variable: - case PConst.Kind.localVariable: - case PConst.Kind.memberVariable: - case PConst.Kind.class: - case PConst.Kind.function: - case PConst.Kind.memberFunction: - case PConst.Kind.keyword: - case PConst.Kind.parameter: - commitCharacters.push('.', ',', ';'); - if (this.completionContext.enableCallCompletions) { - commitCharacters.push('('); - } - break; - } - return commitCharacters.length === 0 ? undefined : commitCharacters; - } -} - -class CompositeCommand implements Command { - public static readonly ID = '_typescript.composite'; - public readonly id = CompositeCommand.ID; - - public execute(...commands: vscode.Command[]) { - for (const command of commands) { - vscode.commands.executeCommand(command.command, ...(command.arguments || [])); - } - } -} - -class CompletionAcceptedCommand implements Command { - public static readonly ID = '_typescript.onCompletionAccepted'; - public readonly id = CompletionAcceptedCommand.ID; - - public constructor( - private readonly onCompletionAccepted: (item: vscode.CompletionItem) => void, - ) { } - - public execute(item: vscode.CompletionItem) { - this.onCompletionAccepted(item); - } -} - -class ApplyCompletionCodeActionCommand implements Command { - public static readonly ID = '_typescript.applyCompletionCodeAction'; - public readonly id = ApplyCompletionCodeActionCommand.ID; - - public constructor( - private readonly client: ITypeScriptServiceClient - ) { } - - public async execute(_file: string, codeActions: Proto.CodeAction[]): Promise { - if (codeActions.length === 0) { - return true; - } - - if (codeActions.length === 1) { - return applyCodeAction(this.client, codeActions[0], nulToken); - } - - interface MyQuickPickItem extends vscode.QuickPickItem { - index: number; - } - - const selection = await vscode.window.showQuickPick( - codeActions.map((action, i): MyQuickPickItem => ({ - label: action.description, - description: '', - index: i - })), { - placeHolder: localize('selectCodeAction', 'Select code action to apply') - } - ); - - if (!selection) { - return false; - } - - const action = codeActions[selection.index]; - if (!action) { - return false; - } - return applyCodeAction(this.client, action, nulToken); - } -} - -interface CompletionConfiguration { - readonly useCodeSnippetsOnMethodSuggest: boolean; - readonly nameSuggestions: boolean; - readonly pathSuggestions: boolean; - readonly autoImportSuggestions: boolean; - readonly includeAutomaticOptionalChainCompletions: boolean; -} - -namespace CompletionConfiguration { - export const useCodeSnippetsOnMethodSuggest = 'suggest.completeFunctionCalls'; - 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, - resource: vscode.Uri - ): CompletionConfiguration { - const config = vscode.workspace.getConfiguration(modeId, resource); - return { - useCodeSnippetsOnMethodSuggest: config.get(CompletionConfiguration.useCodeSnippetsOnMethodSuggest, false), - pathSuggestions: config.get(CompletionConfiguration.pathSuggestions, true), - autoImportSuggestions: config.get(CompletionConfiguration.autoImportSuggestions, true), - nameSuggestions: config.get(CompletionConfiguration.nameSuggestions, true), - includeAutomaticOptionalChainCompletions: config.get(CompletionConfiguration.includeAutomaticOptionalChainCompletions, true), - }; - } -} - -class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider { - - public static readonly triggerCharacters = ['.', '"', '\'', '`', '/', '@', '<']; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly modeId: string, - private readonly typingsStatus: TypingsStatus, - private readonly fileConfigurationManager: FileConfigurationManager, - commandManager: CommandManager, - private readonly telemetryReporter: TelemetryReporter, - onCompletionAccepted: (item: vscode.CompletionItem) => void - ) { - commandManager.register(new ApplyCompletionCodeActionCommand(this.client)); - commandManager.register(new CompositeCommand()); - commandManager.register(new CompletionAcceptedCommand(onCompletionAccepted)); - } - - public async provideCompletionItems( - document: vscode.TextDocument, - position: vscode.Position, - token: vscode.CancellationToken, - context: vscode.CompletionContext - ): Promise { - if (this.typingsStatus.isAcquiringTypings) { - return Promise.reject({ - label: localize( - { key: 'acquiringTypingsLabel', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] }, - 'Acquiring typings...'), - detail: localize( - { key: 'acquiringTypingsDetail', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] }, - 'Acquiring typings definitions for IntelliSense.') - }); - } - - const file = this.client.toOpenedFilePath(document); - if (!file) { - return null; - } - - const line = document.lineAt(position.line); - const completionConfiguration = CompletionConfiguration.getConfigurationForResource(this.modeId, document.uri); - - if (!this.shouldTrigger(context, line, position)) { - return null; - } - - await this.client.interruptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); - - const args: Proto.CompletionsRequestArgs & { includeAutomaticOptionalChainCompletions?: boolean } = { - ...typeConverters.Position.toFileLocationRequestArgs(file, position), - includeExternalModuleExports: completionConfiguration.autoImportSuggestions, - includeInsertTextCompletions: true, - triggerCharacter: this.getTsTriggerCharacter(context), - includeAutomaticOptionalChainCompletions: completionConfiguration.includeAutomaticOptionalChainCompletions, - }; - - let isNewIdentifierLocation = true; - let isIncomplete = false; - let isMemberCompletion = false; - let dotAccessorContext: DotAccessorContext | undefined; - let entries: ReadonlyArray; - let metadata: any | undefined; - if (this.client.apiVersion.gte(API.v300)) { - const startTime = Date.now(); - let response: ServerResponse.Response | 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" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('completions.execute', { - duration: duration + '', - type: response ? response.type : 'unknown', - count: (response && response.type === 'response' && response.body ? response.body.entries.length : 0) + '' - }); - } - - if (response.type !== 'response' || !response.body) { - return null; - } - isNewIdentifierLocation = response.body.isNewIdentifierLocation; - isMemberCompletion = response.body.isMemberCompletion; - if (isMemberCompletion) { - const dotMatch = line.text.slice(0, position.character).match(/\??\.\s*$/) || undefined; - if (dotMatch) { - const range = new vscode.Range(position.translate({ characterDelta: -dotMatch[0].length }), position); - const text = document.getText(range); - dotAccessorContext = { range, text }; - } - } - isIncomplete = (response as any).metadata && (response as any).metadata.isIncomplete; - entries = response.body.entries; - metadata = response.metadata; - } else { - const response = await this.client.interruptGetErr(() => this.client.execute('completions', args, token)); - if (response.type !== 'response' || !response.body) { - return null; - } - - 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)); - return new vscode.CompletionList(items, isIncomplete); - } - - 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; - } - - return context.triggerCharacter as Proto.CompletionsTriggerCharacter; - } - - public async resolveCompletionItem( - item: vscode.CompletionItem, - token: vscode.CancellationToken - ): Promise { - if (!(item instanceof MyCompletionItem)) { - return undefined; - } - - const filepath = this.client.toOpenedFilePath(item.document); - if (!filepath) { - return undefined; - } - - const args: Proto.CompletionDetailsRequestArgs = { - ...typeConverters.Position.toFileLocationRequestArgs(filepath, item.position), - entryNames: [ - item.tsEntry.source ? { name: item.tsEntry.name, source: item.tsEntry.source } : item.tsEntry.name - ] - }; - - const response = await this.client.interruptGetErr(() => this.client.execute('completionEntryDetails', args, token)); - if (response.type !== 'response' || !response.body || !response.body.length) { - return item; - } - - const detail = response.body[0]; - - if (!item.detail && detail.displayParts.length) { - item.detail = Previewer.plain(detail.displayParts); - } - item.documentation = this.getDocumentation(detail, item); - - const codeAction = this.getCodeActions(detail, filepath); - const commands: vscode.Command[] = [{ - command: CompletionAcceptedCommand.ID, - title: '', - arguments: [item] - }]; - if (codeAction.command) { - commands.push(codeAction.command); - } - item.additionalTextEdits = codeAction.additionalTextEdits; - - if (detail && item.useCodeSnippet) { - const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, item.document, token); - if (shouldCompleteFunction) { - const { snippet, parameterCount } = snippetForFunctionCall(item, detail.displayParts); - item.insertText = snippet; - if (parameterCount > 0) { - commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); - } - } - } - - if (commands.length) { - if (commands.length === 1) { - item.command = commands[0]; - } else { - item.command = { - command: CompositeCommand.ID, - title: '', - arguments: commands - }; - } - } - - return item; - } - - private getCodeActions( - detail: Proto.CompletionEntryDetails, - filepath: string - ): { command?: vscode.Command, additionalTextEdits?: vscode.TextEdit[] } { - if (!detail.codeActions || !detail.codeActions.length) { - return {}; - } - - // Try to extract out the additionalTextEdits for the current file. - // Also check if we still have to apply other workspace edits and commands - // using a vscode command - const additionalTextEdits: vscode.TextEdit[] = []; - let hasReaminingCommandsOrEdits = false; - for (const tsAction of detail.codeActions) { - if (tsAction.commands) { - hasReaminingCommandsOrEdits = true; - } - - // Apply all edits in the current file using `additionalTextEdits` - if (tsAction.changes) { - for (const change of tsAction.changes) { - if (change.fileName === filepath) { - additionalTextEdits.push(...change.textChanges.map(typeConverters.TextEdit.fromCodeEdit)); - } else { - hasReaminingCommandsOrEdits = true; - } - } - } - } - - let command: vscode.Command | undefined = undefined; - if (hasReaminingCommandsOrEdits) { - // Create command that applies all edits not in the current file. - command = { - title: '', - command: ApplyCompletionCodeActionCommand.ID, - arguments: [filepath, detail.codeActions.map((x): Proto.CodeAction => ({ - commands: x.commands, - description: x.description, - changes: x.changes.filter(x => x.fileName !== filepath) - }))] - }; - } - - return { - command, - additionalTextEdits: additionalTextEdits.length ? additionalTextEdits : undefined - }; - } - - private isInValidCommitCharacterContext( - document: vscode.TextDocument, - position: vscode.Position - ): boolean { - if (this.client.apiVersion.lt(API.v320)) { - // Workaround for https://github.com/Microsoft/TypeScript/issues/27742 - // Only enable dot completions when previous character not a dot preceeded by whitespace. - // Prevents incorrectly completing while typing spread operators. - if (position.character > 1) { - const preText = document.getText(new vscode.Range( - position.line, 0, - position.line, position.character)); - return preText.match(/(\s|^)\.$/ig) === null; - } - } - - return true; - } - - private shouldTrigger( - context: vscode.CompletionContext, - line: vscode.TextLine, - position: vscode.Position - ): boolean { - if (context.triggerCharacter && this.client.apiVersion.lt(API.v290)) { - if ((context.triggerCharacter === '"' || context.triggerCharacter === '\'')) { - // make sure we are in something that looks like the start of an import - const pre = line.text.slice(0, position.character); - if (!pre.match(/\b(from|import)\s*["']$/) && !pre.match(/\b(import|require)\(['"]$/)) { - return false; - } - } - - if (context.triggerCharacter === '/') { - // make sure we are in something that looks like an import path - const pre = line.text.slice(0, position.character); - if (!pre.match(/\b(from|import)\s*["'][^'"]*$/) && !pre.match(/\b(import|require)\(['"][^'"]*$/)) { - return false; - } - } - - if (context.triggerCharacter === '@') { - // make sure we are in something that looks like the start of a jsdoc comment - const pre = line.text.slice(0, position.character); - if (!pre.match(/^\s*\*[ ]?@/) && !pre.match(/\/\*\*+[ ]?@/)) { - return false; - } - } - - if (context.triggerCharacter === '<') { - return false; - } - } - - return true; - } - - private getDocumentation( - detail: Proto.CompletionEntryDetails, - item: MyCompletionItem - ): vscode.MarkdownString | undefined { - const documentation = new vscode.MarkdownString(); - if (detail.source) { - const importPath = `'${Previewer.plain(detail.source)}'`; - const autoImportLabel = localize('autoImportLabel', 'Auto import from {0}', importPath); - item.detail = `${autoImportLabel}\n${item.detail}`; - } - Previewer.addMarkdownDocumentation(documentation, detail.documentation, detail.tags); - - return documentation.value.length ? documentation : undefined; - } - - private async isValidFunctionCompletionContext( - filepath: string, - position: vscode.Position, - document: vscode.TextDocument, - token: vscode.CancellationToken - ): Promise { - // 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); - const response = await this.client.execute('quickinfo', args, token); - if (response.type === 'response' && response.body) { - switch (response.body.kind) { - case 'var': - case 'let': - case 'const': - case 'alias': - return false; - } - } - } catch { - // Noop - } - - // Don't complete function call if there is already something that looks like a function call - // 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; - } -} - -function shouldExcludeCompletionEntry( - element: Proto.CompletionEntry, - completionConfiguration: CompletionConfiguration -) { - return ( - (!completionConfiguration.nameSuggestions && element.kind === PConst.Kind.warning) - || (!completionConfiguration.pathSuggestions && - (element.kind === PConst.Kind.directory || element.kind === PConst.Kind.script || element.kind === PConst.Kind.externalModuleName)) - || (!completionConfiguration.autoImportSuggestions && element.hasAction) - ); -} - -export function register( - selector: vscode.DocumentSelector, - modeId: string, - client: ITypeScriptServiceClient, - typingsStatus: TypingsStatus, - fileConfigurationManager: FileConfigurationManager, - commandManager: CommandManager, - telemetryReporter: TelemetryReporter, - onCompletionAccepted: (item: vscode.CompletionItem) => void -) { - return new ConfigurationDependentRegistration(modeId, 'suggest.enabled', () => - vscode.languages.registerCompletionItemProvider(selector, - new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager, telemetryReporter, onCompletionAccepted), - ...TypeScriptCompletionItemProvider.triggerCharacters)); -} 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 8c6f09659c332..0000000000000 --- a/extensions/typescript-language-features/src/features/fixAll.ts +++ /dev/null @@ -1,134 +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([ - 2420, // Incorrectly implemented interface - 2552, // Cannot find name -]); - -class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { - - public static readonly metadata: vscode.CodeActionProviderMetadata = { - providedCodeActionKinds: [vscode.CodeActionKind.SourceFixAll] - }; - - 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 { - 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, - token: vscode.CancellationToken, - ): Promise { - await this.fileConfigurationManager.ensureConfigurationForDocument(document, token); - - const autoFixResponse = await this.getAutoFixEdit(file, diagnostics, token); - if (!autoFixResponse) { - return undefined; - } - const { edit, fixedDiagnostics } = autoFixResponse; - const codeAction = new vscode.CodeAction( - localize('autoFix.label', 'Auto fix'), - vscode.CodeActionKind.SourceFixAll); - codeAction.edit = edit; - codeAction.diagnostics = fixedDiagnostics; - - return codeAction; - } - - private async getAutoFixEdit( - file: string, - diagnostics: ReadonlyArray, - 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) { - return undefined; - } - - const fix = response.body[0]; - if (new Set(['fixClassIncorrectlyImplementsInterface', 'spelling']).has(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/folding.ts b/extensions/typescript-language-features/src/features/folding.ts deleted file mode 100644 index 963c4fbb9e061..0000000000000 --- a/extensions/typescript-language-features/src/features/folding.ts +++ /dev/null @@ -1,84 +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 API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -import * as typeConverters from '../utils/typeConverters'; - -class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { - public static readonly minVersion = API.v280; - - public constructor( - private readonly client: ITypeScriptServiceClient - ) { } - - async provideFoldingRanges( - document: vscode.TextDocument, - _context: vscode.FoldingContext, - token: vscode.CancellationToken - ): Promise { - const file = this.client.toOpenedFilePath(document); - if (!file) { - return; - } - - const args: Proto.FileRequestArgs = { file }; - const response = await this.client.execute('getOutliningSpans', args, token); - if (response.type !== 'response' || !response.body) { - return; - } - - return response.body - .map(span => this.convertOutliningSpan(span, document)) - .filter(foldingRange => !!foldingRange) as vscode.FoldingRange[]; - } - - private convertOutliningSpan( - span: Proto.OutliningSpan, - document: vscode.TextDocument - ): vscode.FoldingRange | undefined { - const range = typeConverters.Range.fromTextSpan(span.textSpan); - const kind = TypeScriptFoldingProvider.getFoldingRangeKind(span); - - // Workaround for #49904 - if (span.kind === 'comment') { - const line = document.lineAt(range.start.line).text; - if (line.match(/\/\/\s*#endregion/gi)) { - return undefined; - } - } - - const start = range.start.line; - // workaround for #47240 - const end = (range.end.character > 0 && ['}', ']'].includes(document.getText(new vscode.Range(range.end.translate(0, -1), range.end)))) - ? Math.max(range.end.line - 1, range.start.line) - : range.end.line; - - return new vscode.FoldingRange(start, end, kind); - } - - private static getFoldingRangeKind(span: Proto.OutliningSpan): vscode.FoldingRangeKind | undefined { - switch (span.kind) { - case 'comment': return vscode.FoldingRangeKind.Comment; - case 'region': return vscode.FoldingRangeKind.Region; - case 'imports': return vscode.FoldingRangeKind.Imports; - case 'code': - default: return undefined; - } - } -} - -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient, -): vscode.Disposable { - return new VersionDependentRegistration(client, TypeScriptFoldingProvider.minVersion, () => { - return vscode.languages.registerFoldingRangeProvider(selector, - new TypeScriptFoldingProvider(client)); - }); -} diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/features/formatting.ts deleted file mode 100644 index d64effd1a324a..0000000000000 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ /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. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { ConfigurationDependentRegistration } from '../utils/dependentRegistration'; -import * as typeConverters from '../utils/typeConverters'; -import FileConfigurationManager from './fileConfigurationManager'; - -class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEditProvider, vscode.OnTypeFormattingEditProvider { - public constructor( - private readonly client: ITypeScriptServiceClient, - private readonly formattingOptionsManager: FileConfigurationManager - ) { } - - public async provideDocumentRangeFormattingEdits( - document: vscode.TextDocument, - range: vscode.Range, - options: vscode.FormattingOptions, - token: vscode.CancellationToken - ): Promise { - const file = this.client.toOpenedFilePath(document); - if (!file) { - return undefined; - } - - await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); - - const args = typeConverters.Range.toFormattingRequestArgs(file, range); - const response = await this.client.execute('format', args, token); - if (response.type !== 'response' || !response.body) { - return undefined; - } - - return response.body.map(typeConverters.TextEdit.fromCodeEdit); - } - - public async provideOnTypeFormattingEdits( - document: vscode.TextDocument, - position: vscode.Position, - ch: string, - options: vscode.FormattingOptions, - token: vscode.CancellationToken - ): Promise { - const file = this.client.toOpenedFilePath(document); - if (!file) { - return []; - } - - await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); - - const args: Proto.FormatOnKeyRequestArgs = { - ...typeConverters.Position.toFileLocationRequestArgs(file, position), - key: ch - }; - const response = await this.client.execute('formatonkey', args, token); - if (response.type !== 'response' || !response.body) { - return []; - } - const edits = response.body; - const result: vscode.TextEdit[] = []; - if (!edits) { - return result; - } - for (const edit of edits) { - const textEdit = typeConverters.TextEdit.fromCodeEdit(edit); - const range = textEdit.range; - // 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 === '') { - const lText = document.lineAt(range.start.line).text; - // If the edit leaves something on the line keep the edit (note that the end character is exclusive). - // Keep it also if it removes something else than whitespace - if (lText.trim().length > 0 || lText.length > range.end.character) { - result.push(textEdit); - } - } else { - result.push(textEdit); - } - } - return result; - } -} - -export function register( - selector: vscode.DocumentSelector, - modeId: string, - client: ITypeScriptServiceClient, - fileConfigurationManager: FileConfigurationManager -) { - return new ConfigurationDependentRegistration(modeId, 'format.enable', () => { - const formattingProvider = new TypeScriptFormattingProvider(client, fileConfigurationManager); - return vscode.Disposable.from( - vscode.languages.registerOnTypeFormattingEditProvider(selector, formattingProvider, ';', '}', '\n'), - vscode.languages.registerDocumentRangeFormattingEditProvider(selector, formattingProvider), - ); - }); -} \ No newline at end of file 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 5077f9648a0f3..0000000000000 --- 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 { - 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/implementations.ts b/extensions/typescript-language-features/src/features/implementations.ts deleted file mode 100644 index c7cdeeb755fa1..0000000000000 --- a/extensions/typescript-language-features/src/features/implementations.ts +++ /dev/null @@ -1,22 +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 { ITypeScriptServiceClient } from '../typescriptService'; -import DefinitionProviderBase from './definitionProviderBase'; - -class TypeScriptImplementationProvider extends DefinitionProviderBase implements vscode.ImplementationProvider { - public provideImplementation(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - return this.getSymbolLocations('implementation', document, position, token); - } -} - -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient, -) { - return vscode.languages.registerImplementationProvider(selector, - new TypeScriptImplementationProvider(client)); -} diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts deleted file mode 100644 index 1b1ad27da858d..0000000000000 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ /dev/null @@ -1,347 +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 { nulToken } from '../utils/cancellation'; -import { applyCodeActionCommands, getEditForCodeAction } from '../utils/codeAction'; -import { Command, CommandManager } from '../utils/commandManager'; -import { memoize } from '../utils/memoize'; -import TelemetryReporter from '../utils/telemetry'; -import * as typeConverters from '../utils/typeConverters'; -import { DiagnosticsManager } from './diagnostics'; -import FileConfigurationManager from './fileConfigurationManager'; - -const localize = nls.loadMessageBundle(); - -class ApplyCodeActionCommand implements Command { - public static readonly ID = '_typescript.applyCodeActionCommand'; - public readonly id = ApplyCodeActionCommand.ID; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly telemetryReporter: TelemetryReporter, - ) { } - - public async execute( - action: Proto.CodeFixAction - ): Promise { - /* __GDPR__ - "quickFix.execute" : { - "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('quickFix.execute', { - fixName: action.fixName - }); - - return applyCodeActionCommands(this.client, action.commands, nulToken); - } -} - - -class ApplyFixAllCodeAction implements Command { - public static readonly ID = '_typescript.applyFixAllCodeAction'; - public readonly id = ApplyFixAllCodeAction.ID; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly telemetryReporter: TelemetryReporter, - ) { } - - public async execute( - file: string, - tsAction: Proto.CodeFixAction, - ): Promise { - if (!tsAction.fixId) { - return; - } - - /* __GDPR__ - "quickFixAll.execute" : { - "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('quickFixAll.execute', { - fixName: tsAction.fixName - }); - - const args: Proto.GetCombinedCodeFixRequestArgs = { - scope: { - type: 'file', - args: { file } - }, - fixId: tsAction.fixId, - }; - - const response = await this.client.execute('getCombinedCodeFix', args, nulToken); - if (response.type !== 'response' || !response.body) { - return undefined; - } - - const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes); - await vscode.workspace.applyEdit(edit); - await applyCodeActionCommands(this.client, response.body.commands, nulToken); - } -} - -/** - * Unique set of diagnostics keyed on diagnostic range and error code. - */ -class DiagnosticsSet { - public static from(diagnostics: vscode.Diagnostic[]) { - const values = new Map(); - for (const diagnostic of diagnostics) { - values.set(DiagnosticsSet.key(diagnostic), diagnostic); - } - return new DiagnosticsSet(values); - } - - private static key(diagnostic: vscode.Diagnostic) { - const { start, end } = diagnostic.range; - return `${diagnostic.code}-${start.line},${start.character}-${end.line},${end.character}`; - } - - private constructor( - private readonly _values: Map - ) { } - - public get values(): Iterable { - return this._values.values(); - } - - public get size() { - return this._values.size; - } -} - -class CodeActionSet { - private readonly _actions = new Set(); - private readonly _fixAllActions = new Map<{}, vscode.CodeAction>(); - - public get values(): Iterable { - return this._actions; - } - - public addAction(action: vscode.CodeAction) { - this._actions.add(action); - } - - public addFixAllAction(fixId: {}, action: vscode.CodeAction) { - const existing = this._fixAllActions.get(fixId); - if (existing) { - // reinsert action at back of actions list - this._actions.delete(existing); - } - this.addAction(action); - this._fixAllActions.set(fixId, action); - } - - public hasFixAllAction(fixId: {}) { - return this._fixAllActions.has(fixId); - } -} - -class SupportedCodeActionProvider { - public constructor( - private readonly client: ITypeScriptServiceClient - ) { } - - public async getFixableDiagnosticsForContext(context: vscode.CodeActionContext): Promise { - const fixableCodes = await this.fixableDiagnosticCodes; - return DiagnosticsSet.from( - context.diagnostics.filter(diagnostic => typeof diagnostic.code !== 'undefined' && fixableCodes.has(diagnostic.code + ''))); - } - - @memoize - private get fixableDiagnosticCodes(): Thenable> { - return this.client.execute('getSupportedCodeFixes', null, nulToken) - .then(response => response.type === 'response' ? response.body || [] : []) - .then(codes => new Set(codes)); - } -} - -class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { - - public static readonly metadata: vscode.CodeActionProviderMetadata = { - providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] - }; - - private readonly supportedCodeActionProvider: SupportedCodeActionProvider; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly formattingConfigurationManager: FileConfigurationManager, - commandManager: CommandManager, - private readonly diagnosticsManager: DiagnosticsManager, - telemetryReporter: TelemetryReporter - ) { - commandManager.register(new ApplyCodeActionCommand(client, telemetryReporter)); - commandManager.register(new ApplyFixAllCodeAction(client, telemetryReporter)); - - this.supportedCodeActionProvider = new SupportedCodeActionProvider(client); - } - - public async provideCodeActions( - document: vscode.TextDocument, - _range: vscode.Range, - context: vscode.CodeActionContext, - token: vscode.CancellationToken - ): Promise { - const file = this.client.toOpenedFilePath(document); - if (!file) { - return []; - } - - const fixableDiagnostics = await this.supportedCodeActionProvider.getFixableDiagnosticsForContext(context); - if (!fixableDiagnostics.size) { - return []; - } - - if (this.client.bufferSyncSupport.hasPendingDiagnostics(document.uri)) { - return []; - } - - await this.formattingConfigurationManager.ensureConfigurationForDocument(document, token); - - const results = new CodeActionSet(); - for (const diagnostic of fixableDiagnostics.values) { - await this.getFixesForDiagnostic(document, file, diagnostic, results, token); - } - return Array.from(results.values); - } - - private async getFixesForDiagnostic( - document: vscode.TextDocument, - file: string, - diagnostic: vscode.Diagnostic, - results: CodeActionSet, - token: vscode.CancellationToken, - ): Promise { - 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) { - return results; - } - - for (const tsCodeFix of response.body) { - this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix as Proto.CodeFixAction); - } - return results; - } - - private addAllFixesForTsCodeAction( - results: CodeActionSet, - document: vscode.TextDocument, - file: string, - diagnostic: vscode.Diagnostic, - tsAction: Proto.CodeFixAction - ): CodeActionSet { - results.addAction(this.getSingleFixForTsCodeAction(diagnostic, tsAction)); - this.addFixAllForTsCodeAction(results, document, file, diagnostic, tsAction as Proto.CodeFixAction); - return results; - } - - private getSingleFixForTsCodeAction( - diagnostic: vscode.Diagnostic, - tsAction: Proto.CodeFixAction - ): vscode.CodeAction { - const codeAction = new vscode.CodeAction(tsAction.description, vscode.CodeActionKind.QuickFix); - codeAction.edit = getEditForCodeAction(this.client, tsAction); - codeAction.diagnostics = [diagnostic]; - codeAction.command = { - command: ApplyCodeActionCommand.ID, - arguments: [tsAction], - title: '' - }; - codeAction.isPreferred = isPreferredFix(tsAction); - return codeAction; - } - - private addFixAllForTsCodeAction( - results: CodeActionSet, - document: vscode.TextDocument, - file: string, - diagnostic: vscode.Diagnostic, - tsAction: Proto.CodeFixAction, - ): CodeActionSet { - if (!tsAction.fixId || this.client.apiVersion.lt(API.v270) || results.hasFixAllAction(tsAction.fixId)) { - return results; - } - - // Make sure there are multiple diagnostics of the same type in the file - if (!this.diagnosticsManager.getDiagnostics(document.uri).some(x => { - if (x === diagnostic) { - return false; - } - return x.code === diagnostic.code - || (fixAllErrorCodes.has(x.code as number) && fixAllErrorCodes.get(x.code as number) === fixAllErrorCodes.get(diagnostic.code as number)); - })) { - return results; - } - - const action = new vscode.CodeAction( - tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), - vscode.CodeActionKind.QuickFix); - action.diagnostics = [diagnostic]; - action.command = { - command: ApplyFixAllCodeAction.ID, - arguments: [file, tsAction], - title: '' - }; - results.addFixAllAction(tsAction.fixId, action); - return results; - } -} - -// Some fix all actions can actually fix multiple differnt diagnostics. Make sure we still show the fix all action -// in such cases -const fixAllErrorCodes = new Map([ - // Missing async - [2339, 2339], - [2345, 2339], -]); - - -const preferredFixes = new Set([ - 'annotateWithTypeFromJSDoc', - 'constructorForDerivedNeedSuperCall', - 'extendsInterfaceBecomesImplements', - 'fixAwaitInSyncFunction', - 'fixClassIncorrectlyImplementsInterface', - 'fixUnreachableCode', - 'forgottenThisPropertyAccess', - 'spelling', - 'unusedIdentifier', - 'addMissingAwait', -]); -function isPreferredFix(tsAction: Proto.CodeFixAction): boolean { - return preferredFixes.has(tsAction.fixName); -} - -export function register( - selector: vscode.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); -} diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts deleted file mode 100644 index 9b7bd25828097..0000000000000 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ /dev/null @@ -1,264 +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 { nulToken } from '../utils/cancellation'; -import { Command, CommandManager } from '../utils/commandManager'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; -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(); - - -class ApplyRefactoringCommand implements Command { - public static readonly ID = '_typescript.applyRefactoring'; - public readonly id = ApplyRefactoringCommand.ID; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly telemetryReporter: TelemetryReporter - ) { } - - public async execute( - document: vscode.TextDocument, - refactor: string, - action: string, - range: vscode.Range - ): Promise { - const file = this.client.toOpenedFilePath(document); - if (!file) { - return false; - } - - /* __GDPR__ - "refactor.execute" : { - "action" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('refactor.execute', { - action: 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) { - vscode.window.showErrorMessage(localize('refactoringFailed', "Could not apply refactoring")); - return false; - } - - const workspaceEdit = await this.toWorkspaceEdit(response.body); - if (!(await vscode.workspace.applyEdit(workspaceEdit))) { - return false; - } - - const renameLocation = response.body.renameLocation; - if (renameLocation) { - await vscode.commands.executeCommand('editor.action.rename', [ - 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; - } -} - -class SelectRefactorCommand implements Command { - public static readonly ID = '_typescript.selectRefactoring'; - public readonly id = SelectRefactorCommand.ID; - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly doRefactoring: ApplyRefactoringCommand - ) { } - - public async execute( - document: vscode.TextDocument, - info: Proto.ApplicableRefactorInfo, - range: vscode.Range - ): Promise { - const file = this.client.toOpenedFilePath(document); - if (!file) { - return false; - } - const selected = await vscode.window.showQuickPick(info.actions.map((action): vscode.QuickPickItem => ({ - label: action.name, - description: action.description, - }))); - if (!selected) { - return false; - } - return this.doRefactoring.execute(document, info.name, selected.label, range); - } -} - -class TypeScriptRefactorProvider implements vscode.CodeActionProvider { - public static readonly minVersion = API.v240; - - private static readonly extractFunctionKind = vscode.CodeActionKind.RefactorExtract.append('function'); - private static readonly extractConstantKind = vscode.CodeActionKind.RefactorExtract.append('constant'); - private static readonly extractTypeKind = vscode.CodeActionKind.RefactorExtract.append('type'); - private static readonly moveKind = vscode.CodeActionKind.Refactor.append('move'); - - constructor( - private readonly client: ITypeScriptServiceClient, - private readonly formattingOptionsManager: FormattingOptionsManager, - commandManager: CommandManager, - telemetryReporter: TelemetryReporter - ) { - const doRefactoringCommand = commandManager.register(new ApplyRefactoringCommand(this.client, telemetryReporter)); - commandManager.register(new SelectRefactorCommand(this.client, doRefactoringCommand)); - } - - public static readonly metadata: vscode.CodeActionProviderMetadata = { - providedCodeActionKinds: [vscode.CodeActionKind.Refactor], - }; - - public async provideCodeActions( - document: vscode.TextDocument, - rangeOrSelection: vscode.Range | vscode.Selection, - context: vscode.CodeActionContext, - token: vscode.CancellationToken - ): Promise { - if (!this.shouldTrigger(rangeOrSelection, context)) { - return undefined; - } - if (!this.client.toOpenedFilePath(document)) { - return undefined; - } - - const response = await this.client.interruptGetErr(() => { - const file = this.client.toOpenedFilePath(document); - if (!file) { - return undefined; - } - this.formattingOptionsManager.ensureConfigurationForDocument(document, token); - - const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection); - return this.client.execute('getApplicableRefactors', args, token); - }); - if (!response || response.type !== 'response' || !response.body) { - return undefined; - } - - return this.convertApplicableRefactors(response.body, document, rangeOrSelection); - } - - private convertApplicableRefactors( - body: Proto.ApplicableRefactorInfo[], - document: vscode.TextDocument, - rangeOrSelection: vscode.Range | vscode.Selection - ) { - const actions: vscode.CodeAction[] = []; - 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] - }; - actions.push(codeAction); - } else { - for (const action of info.actions) { - actions.push(this.refactorActionToCodeAction(action, document, info, rangeOrSelection)); - } - } - } - return actions; - } - - private refactorActionToCodeAction( - action: Proto.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); - return codeAction; - } - - private shouldTrigger(rangeOrSelection: vscode.Range | vscode.Selection, context: vscode.CodeActionContext) { - if (context.only && !vscode.CodeActionKind.Refactor.contains(context.only)) { - return false; - } - - return rangeOrSelection instanceof vscode.Selection; - } - - private static getKind(refactor: Proto.RefactorActionInfo) { - if (refactor.name.startsWith('function_')) { - return TypeScriptRefactorProvider.extractFunctionKind; - } else if (refactor.name.startsWith('constant_')) { - return TypeScriptRefactorProvider.extractConstantKind; - } else if (refactor.name.startsWith('Move')) { - return TypeScriptRefactorProvider.moveKind; - } else if (refactor.name.includes('Extract to type alias')) { - return TypeScriptRefactorProvider.extractTypeKind; - } - return vscode.CodeActionKind.Refactor; - } - - private static isPreferred( - action: Proto.RefactorActionInfo - ): boolean { - if (action.name.startsWith('constant_')) { - return action.name.endsWith('scope_0'); - } - if (action.name.includes('Extract to type alias')) { - return true; - } - return false; - } -} - -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient, - formattingOptionsManager: FormattingOptionsManager, - commandManager: CommandManager, - telemetryReporter: TelemetryReporter, -) { - return new VersionDependentRegistration(client, TypeScriptRefactorProvider.minVersion, () => { - return vscode.languages.registerCodeActionsProvider(selector, - 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/features/references.ts deleted file mode 100644 index d77ecc5b10b2b..0000000000000 --- a/extensions/typescript-language-features/src/features/references.ts +++ /dev/null @@ -1,50 +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 { ITypeScriptServiceClient } from '../typescriptService'; -import * as typeConverters from '../utils/typeConverters'; - -class TypeScriptReferenceSupport implements vscode.ReferenceProvider { - public constructor( - private readonly client: ITypeScriptServiceClient) { } - - public async provideReferences( - document: vscode.TextDocument, - position: vscode.Position, - options: vscode.ReferenceContext, - token: vscode.CancellationToken - ): Promise { - const filepath = this.client.toOpenedFilePath(document); - if (!filepath) { - return []; - } - - const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); - const response = await this.client.execute('references', args, token); - if (response.type !== 'response' || !response.body) { - return []; - } - - const result: vscode.Location[] = []; - for (const ref of response.body.refs) { - if (!options.includeDeclaration && ref.isDefinition) { - continue; - } - const url = this.client.toResource(ref.file); - const location = typeConverters.Location.fromTextSpan(url, ref); - result.push(location); - } - return result; - } -} - -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient -) { - return vscode.languages.registerReferenceProvider(selector, - new TypeScriptReferenceSupport(client)); -} diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts deleted file mode 100644 index 25b8d117b9c4d..0000000000000 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ /dev/null @@ -1,103 +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 * 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 { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; - -const localize = nls.loadMessageBundle(); - -class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvider { - public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise { - 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 }); - if (response.type !== 'response' || !response.body) { - codeLens.command = response.type === 'cancelled' - ? TypeScriptBaseCodeLensProvider.cancelledCommand - : TypeScriptBaseCodeLensProvider.errorCommand; - return codeLens; - } - - const locations = response.body.refs - .map(reference => - typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)) - .filter(location => - // Exclude original definition from references - !(location.uri.toString() === codeLens.document.toString() && - location.range.start.isEqual(codeLens.range.start))); - - codeLens.command = { - title: this.getCodeLensLabel(locations), - command: locations.length ? 'editor.action.showReferences' : '', - arguments: [codeLens.document, codeLens.range.start, locations] - }; - return codeLens; - } - - private getCodeLensLabel(locations: ReadonlyArray): string { - return locations.length === 1 - ? localize('oneReferenceLabel', '1 reference') - : localize('manyReferenceLabel', '{0} references', locations.length); - } - - protected extractSymbol( - document: vscode.TextDocument, - item: Proto.NavigationTree, - parent: Proto.NavigationTree | null - ): vscode.Range | null { - if (parent && parent.kind === PConst.Kind.enum) { - return getSymbolRange(document, item); - } - - switch (item.kind) { - case PConst.Kind.const: - case PConst.Kind.let: - case PConst.Kind.variable: - case PConst.Kind.function: - // Only show references for exported variables - if (!item.kindModifiers.match(/\bexport\b/)) { - break; - } - // fallthrough - - case PConst.Kind.class: - if (item.text === '') { - break; - } - // fallthrough - - case PConst.Kind.memberFunction: - case PConst.Kind.memberVariable: - case PConst.Kind.memberGetAccessor: - case PConst.Kind.memberSetAccessor: - case PConst.Kind.constructorImplementation: - case PConst.Kind.interface: - case PConst.Kind.type: - case PConst.Kind.enum: - return getSymbolRange(document, item); - } - - return null; - } -} - -export function register( - selector: vscode.DocumentSelector, - modeId: string, - client: ITypeScriptServiceClient, - cachedResponse: CachedResponse, -) { - return new ConfigurationDependentRegistration(modeId, 'referencesCodeLens.enabled', () => { - return vscode.languages.registerCodeLensProvider(selector, - new TypeScriptReferencesCodeLensProvider(client, cachedResponse)); - }); -} diff --git a/extensions/typescript-language-features/src/features/signatureHelp.ts b/extensions/typescript-language-features/src/features/signatureHelp.ts deleted file mode 100644 index f7ad9ad7ee969..0000000000000 --- a/extensions/typescript-language-features/src/features/signatureHelp.ts +++ /dev/null @@ -1,118 +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 * as Previewer from '../utils/previewer'; -import * as typeConverters from '../utils/typeConverters'; - -class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { - - public static readonly triggerCharacters = ['(', ',', '<']; - public static readonly retriggerCharacters = [')']; - - public constructor( - private readonly client: ITypeScriptServiceClient - ) { } - - public async provideSignatureHelp( - document: vscode.TextDocument, - position: vscode.Position, - token: vscode.CancellationToken, - context: vscode.SignatureHelpContext, - ): Promise { - const filepath = this.client.toOpenedFilePath(document); - if (!filepath) { - return undefined; - } - - const args: Proto.SignatureHelpRequestArgs = { - ...typeConverters.Position.toFileLocationRequestArgs(filepath, position), - triggerReason: toTsTriggerReason(context) - }; - const response = await this.client.interruptGetErr(() => this.client.execute('signatureHelp', args, token)); - if (response.type !== 'response' || !response.body) { - return undefined; - } - - 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)); - - return result; - } - - private getActiveParmeter(info: Proto.SignatureHelpItems): number { - const activeSignature = info.items[info.selectedItemIndex]; - if (activeSignature && activeSignature.isVariadic) { - return Math.min(info.argumentIndex, activeSignature.parameters.length - 1); - } - return info.argumentIndex; - } - - private convertSignature(item: Proto.SignatureHelpItem) { - const signature = new vscode.SignatureInformation( - Previewer.plain(item.prefixDisplayParts), - Previewer.markdownDocumentation(item.documentation, item.tags.filter(x => x.name !== 'param'))); - - let textIndex = signature.label.length; - const separatorLabel = Previewer.plain(item.separatorDisplayParts); - for (let i = 0; i < item.parameters.length; ++i) { - const parameter = item.parameters[i]; - const label = Previewer.plain(parameter.displayParts); - - signature.parameters.push( - new vscode.ParameterInformation( - [textIndex, textIndex + label.length], - Previewer.markdownDocumentation(parameter.documentation, []))); - - textIndex += label.length; - signature.label += label; - - if (i !== item.parameters.length - 1) { - signature.label += separatorLabel; - textIndex += separatorLabel.length; - } - } - - signature.label += Previewer.plain(item.suffixDisplayParts); - return signature; - } -} - -function toTsTriggerReason(context: vscode.SignatureHelpContext): Proto.SignatureHelpTriggerReason { - switch (context.triggerKind) { - case vscode.SignatureHelpTriggerKind.TriggerCharacter: - if (context.triggerCharacter) { - if (context.isRetrigger) { - return { kind: 'retrigger', triggerCharacter: context.triggerCharacter as any }; - } else { - return { kind: 'characterTyped', triggerCharacter: context.triggerCharacter as any }; - } - } else { - return { kind: 'invoked' }; - } - - case vscode.SignatureHelpTriggerKind.ContentChange: - return context.isRetrigger ? { kind: 'retrigger' } : { kind: 'invoked' }; - - case vscode.SignatureHelpTriggerKind.Invoke: - default: - return { kind: 'invoked' }; - } -} -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient, -) { - return vscode.languages.registerSignatureHelpProvider(selector, - new TypeScriptSignatureHelpProvider(client), { - triggerCharacters: TypeScriptSignatureHelpProvider.triggerCharacters, - retriggerCharacters: TypeScriptSignatureHelpProvider.retriggerCharacters - }); -} diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/features/tagClosing.ts deleted file mode 100644 index 1123b17553111..0000000000000 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ /dev/null @@ -1,175 +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 API from '../utils/api'; -import { ConditionalRegistration, ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; -import { Disposable } from '../utils/dispose'; -import * as typeConverters from '../utils/typeConverters'; - -class TagClosing extends Disposable { - public static readonly minVersion = API.v300; - - private _disposed = false; - private _timeout: NodeJS.Timer | undefined = undefined; - private _cancel: vscode.CancellationTokenSource | undefined = undefined; - - constructor( - private readonly client: ITypeScriptServiceClient - ) { - super(); - vscode.workspace.onDidChangeTextDocument( - event => this.onDidChangeTextDocument(event.document, event.contentChanges), - null, - this._disposables); - } - - public dispose() { - super.dispose(); - this._disposed = true; - - if (this._timeout) { - clearTimeout(this._timeout); - this._timeout = undefined; - } - - if (this._cancel) { - this._cancel.cancel(); - this._cancel.dispose(); - this._cancel = undefined; - } - } - - private onDidChangeTextDocument( - document: vscode.TextDocument, - changes: readonly vscode.TextDocumentContentChangeEvent[] - ) { - const activeDocument = vscode.window.activeTextEditor && vscode.window.activeTextEditor.document; - if (document !== activeDocument || changes.length === 0) { - return; - } - - const filepath = this.client.toOpenedFilePath(document); - if (!filepath) { - return; - } - - if (typeof this._timeout !== 'undefined') { - clearTimeout(this._timeout); - } - - if (this._cancel) { - this._cancel.cancel(); - this._cancel.dispose(); - this._cancel = undefined; - } - - const lastChange = changes[changes.length - 1]; - const lastCharacter = lastChange.text[lastChange.text.length - 1]; - if (lastChange.rangeLength > 0 || lastCharacter !== '>' && lastCharacter !== '/') { - return; - } - - const priorCharacter = lastChange.range.start.character > 0 - ? document.getText(new vscode.Range(lastChange.range.start.translate({ characterDelta: -1 }), lastChange.range.start)) - : ''; - if (priorCharacter === '>') { - return; - } - - const version = document.version; - this._timeout = setTimeout(async () => { - this._timeout = undefined; - - if (this._disposed) { - return; - } - - const addedLines = lastChange.text.split(/\r\n|\n/g); - const position = addedLines.length <= 1 - ? lastChange.range.start.translate({ characterDelta: lastChange.text.length }) - : new vscode.Position(lastChange.range.start.line + addedLines.length - 1, addedLines[addedLines.length - 1].length); - - const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); - this._cancel = new vscode.CancellationTokenSource(); - const response = await this.client.execute('jsxClosingTag', args, this._cancel.token); - if (response.type !== 'response' || !response.body) { - return; - } - - if (this._disposed) { - return; - } - - const activeEditor = vscode.window.activeTextEditor; - if (!activeEditor) { - return; - } - - const insertion = response.body; - const activeDocument = activeEditor.document; - if (document === activeDocument && activeDocument.version === version) { - activeEditor.insertSnippet( - this.getTagSnippet(insertion), - this.getInsertionPositions(activeEditor, position)); - } - }, 100); - } - - private getTagSnippet(closingTag: Proto.TextInsertion): vscode.SnippetString { - const snippet = new vscode.SnippetString(); - snippet.appendPlaceholder('', 0); - snippet.appendText(closingTag.newText); - return snippet; - } - - private getInsertionPositions(editor: vscode.TextEditor, position: vscode.Position) { - const activeSelectionPositions = editor.selections.map(s => s.active); - return activeSelectionPositions.some(p => p.isEqual(position)) - ? activeSelectionPositions - : position; - } -} - -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(); - } - } -} - -export function register( - selector: vscode.DocumentSelector, - modeId: string, - client: ITypeScriptServiceClient, -) { - return new VersionDependentRegistration(client, TagClosing.minVersion, () => - new ConfigurationDependentRegistration(modeId, 'autoClosingTags', () => - new ActiveDocumentDependentRegistration(selector, () => - new TagClosing(client)))); -} diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts deleted file mode 100644 index e41a4b66d0d4e..0000000000000 --- a/extensions/typescript-language-features/src/features/task.ts +++ /dev/null @@ -1,293 +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 * as jsonc from 'jsonc-parser'; -import * as path from 'path'; -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import { isTsConfigFileName } from '../utils/languageDescription'; -import { Lazy } from '../utils/lazy'; -import { isImplicitProjectConfigFile } from '../utils/tsconfig'; -import TsConfigProvider, { TSConfig } from '../utils/tsconfigProvider'; - -const localize = nls.loadMessageBundle(); - -type AutoDetect = 'on' | 'off' | 'build' | 'watch'; - - -const exists = (file: string): Promise => - new Promise((resolve, _reject) => { - fs.exists(file, (value: boolean) => { - resolve(value); - }); - }); - - -interface TypeScriptTaskDefinition extends vscode.TaskDefinition { - tsconfig: string; - option?: string; -} - -/** - * Provides tasks for building `tsconfig.json` files in a project. - */ -export default class TscTaskProvider implements vscode.TaskProvider { - private autoDetect: AutoDetect = 'on'; - private readonly tsconfigProvider: TsConfigProvider; - private readonly disposables: vscode.Disposable[] = []; - - public constructor( - private readonly client: Lazy - ) { - this.tsconfigProvider = new TsConfigProvider(); - - vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this, this.disposables); - this.onConfigurationChanged(); - } - - dispose() { - this.disposables.forEach(x => x.dispose()); - } - - public async provideTasks(token: vscode.CancellationToken): Promise { - const folders = vscode.workspace.workspaceFolders; - if ((this.autoDetect === 'off') || !folders || !folders.length) { - return []; - } - - const configPaths: Set = new Set(); - const tasks: vscode.Task[] = []; - for (const project of await this.getAllTsConfigs(token)) { - if (!configPaths.has(project.path)) { - configPaths.add(project.path); - tasks.push(...(await this.getTasksForProject(project))); - } - } - return tasks; - } - - public async resolveTask(_task: vscode.Task): Promise { - const definition = _task.definition; - const badTsconfig = /\\tsconfig.*\.json/; - if (badTsconfig.exec(definition.tsconfig) !== null) { - // Warn that the task has the wrong slash type - vscode.window.showWarningMessage(localize('badTsConfig', "TypeScript Task in tasks.json contains \"\\\\\". TypeScript tasks tsconfig must use \"/\"")); - return undefined; - } - - const typescriptTask = (_task.definition).tsconfig; - if (typescriptTask) { - if (_task.scope === undefined || _task.scope === vscode.TaskScope.Global || _task.scope === vscode.TaskScope.Workspace) { - // scope is required to be a WorkspaceFolder for resolveTask - return undefined; - } - const kind: TypeScriptTaskDefinition = (_task.definition); - const tsconfigUri: vscode.Uri = _task.scope.uri.with({ path: _task.scope.uri.path + '/' + kind.tsconfig }); - const tsconfig: TSConfig = { - path: tsconfigUri.fsPath, - posixPath: tsconfigUri.path, - workspaceFolder: _task.scope - }; - return this.getTasksForProjectAndDefinition(tsconfig, kind); - } - return undefined; - } - - private async getAllTsConfigs(token: vscode.CancellationToken): Promise { - const out = new Set(); - const configs = [ - ...await this.getTsConfigForActiveFile(token), - ...await this.getTsConfigsInWorkspace() - ]; - for (const config of configs) { - if (await exists(config.path)) { - out.add(config); - } - } - return Array.from(out); - } - - private async getTsConfigForActiveFile(token: vscode.CancellationToken): Promise { - const editor = vscode.window.activeTextEditor; - if (editor) { - if (isTsConfigFileName(editor.document.fileName)) { - const uri = editor.document.uri; - return [{ - path: uri.fsPath, - posixPath: uri.path, - workspaceFolder: vscode.workspace.getWorkspaceFolder(uri) - }]; - } - } - - const file = this.getActiveTypeScriptFile(); - if (!file) { - return []; - } - - const response = await this.client.value.execute( - 'projectInfo', - { file, needFileNameList: false }, - token); - if (response.type !== 'response' || !response.body) { - return []; - } - - const { configFileName } = response.body; - if (configFileName && !isImplicitProjectConfigFile(configFileName)) { - const normalizedConfigPath = path.normalize(configFileName); - const uri = vscode.Uri.file(normalizedConfigPath); - const folder = vscode.workspace.getWorkspaceFolder(uri); - return [{ - path: normalizedConfigPath, - posixPath: uri.path, - workspaceFolder: folder - }]; - } - - return []; - } - - private async getTsConfigsInWorkspace(): Promise { - return Array.from(await this.tsconfigProvider.getConfigsForWorkspace()); - } - - private static async getCommand(project: TSConfig): Promise { - if (project.workspaceFolder) { - const localTsc = await TscTaskProvider.getLocalTscAtPath(path.dirname(project.path)); - if (localTsc) { - return localTsc; - } - - const workspaceTsc = await TscTaskProvider.getLocalTscAtPath(project.workspaceFolder.uri.fsPath); - if (workspaceTsc) { - return workspaceTsc; - } - } - - // Use global tsc version - return 'tsc'; - } - - private static async getLocalTscAtPath(folderPath: string): Promise { - const platform = process.platform; - const bin = path.join(folderPath, 'node_modules', '.bin'); - if (platform === 'win32' && await exists(path.join(bin, 'tsc.cmd'))) { - return path.join(bin, 'tsc.cmd'); - } else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(bin, 'tsc'))) { - return path.join(bin, 'tsc'); - } - return undefined; - } - - private getActiveTypeScriptFile(): string | undefined { - const editor = vscode.window.activeTextEditor; - if (editor) { - const document = editor.document; - if (document && (document.languageId === 'typescript' || document.languageId === 'typescriptreact')) { - return this.client.value.toPath(document.uri); - } - } - return undefined; - } - - private getBuildTask(workspaceFolder: vscode.WorkspaceFolder | undefined, label: string, command: string, args: string[], buildTaskidentifier: TypeScriptTaskDefinition): vscode.Task { - const buildTask = new vscode.Task( - buildTaskidentifier, - workspaceFolder || vscode.TaskScope.Workspace, - localize('buildTscLabel', 'build - {0}', label), - 'tsc', - new vscode.ShellExecution(command, args), - '$tsc'); - buildTask.group = vscode.TaskGroup.Build; - buildTask.isBackground = false; - return buildTask; - } - - private getWatchTask(workspaceFolder: vscode.WorkspaceFolder | undefined, label: string, command: string, args: string[], watchTaskidentifier: TypeScriptTaskDefinition) { - const watchTask = new vscode.Task( - watchTaskidentifier, - workspaceFolder || vscode.TaskScope.Workspace, - localize('buildAndWatchTscLabel', 'watch - {0}', label), - 'tsc', - new vscode.ShellExecution(command, [...args, '--watch']), - '$tsc-watch'); - watchTask.group = vscode.TaskGroup.Build; - watchTask.isBackground = true; - return watchTask; - } - - private async getTasksForProject(project: TSConfig): Promise { - const command = await TscTaskProvider.getCommand(project); - const args = await this.getBuildShellArgs(project); - const label = this.getLabelForTasks(project); - - const tasks: vscode.Task[] = []; - - if (this.autoDetect === 'build' || this.autoDetect === 'on') { - tasks.push(this.getBuildTask(project.workspaceFolder, label, command, args, { type: 'typescript', tsconfig: label })); - } - - if (this.autoDetect === 'watch' || this.autoDetect === 'on') { - - tasks.push(this.getWatchTask(project.workspaceFolder, label, command, args, { type: 'typescript', tsconfig: label, option: 'watch' })); - } - - return tasks; - } - - private async getTasksForProjectAndDefinition(project: TSConfig, definition: TypeScriptTaskDefinition): Promise { - const command = await TscTaskProvider.getCommand(project); - const args = await this.getBuildShellArgs(project); - const label = this.getLabelForTasks(project); - - let task: vscode.Task | undefined; - - if (definition.option === undefined) { - task = this.getBuildTask(project.workspaceFolder, label, command, args, definition); - } else if (definition.option === 'watch') { - task = this.getWatchTask(project.workspaceFolder, label, command, args, definition); - } - - return task; - } - - private getBuildShellArgs(project: TSConfig): Promise> { - const defaultArgs = ['-p', project.path]; - return new Promise>((resolve) => { - fs.readFile(project.path, (error, result) => { - if (error) { - return resolve(defaultArgs); - } - - try { - const tsconfig = jsonc.parse(result.toString()); - if (tsconfig.references) { - return resolve(['-b', project.path]); - } - } catch { - // noop - } - return resolve(defaultArgs); - }); - }); - } - - private getLabelForTasks(project: TSConfig): string { - if (project.workspaceFolder) { - const workspaceNormalizedUri = vscode.Uri.file(path.normalize(project.workspaceFolder.uri.fsPath)); // Make sure the drive letter is lowercase - return path.posix.relative(workspaceNormalizedUri.path, project.posixPath); - } - - return project.posixPath; - } - - private onConfigurationChanged(): void { - const type = vscode.workspace.getConfiguration('typescript.tsc').get('autoDetect'); - this.autoDetect = typeof type === 'undefined' ? 'on' : type; - } -} diff --git a/extensions/typescript-language-features/src/features/typeDefinitions.ts b/extensions/typescript-language-features/src/features/typeDefinitions.ts deleted file mode 100644 index 6f63e44df0a4c..0000000000000 --- a/extensions/typescript-language-features/src/features/typeDefinitions.ts +++ /dev/null @@ -1,22 +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 { ITypeScriptServiceClient } from '../typescriptService'; -import DefinitionProviderBase from './definitionProviderBase'; - -export default class TypeScriptTypeDefinitionProvider extends DefinitionProviderBase implements vscode.TypeDefinitionProvider { - public provideTypeDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - return this.getSymbolLocations('typeDefinition', document, position, token); - } -} - -export function register( - selector: vscode.DocumentSelector, - client: ITypeScriptServiceClient, -) { - return vscode.languages.registerTypeDefinitionProvider(selector, - new TypeScriptTypeDefinitionProvider(client)); -} diff --git a/extensions/typescript-language-features/src/features/workspaceSymbols.ts b/extensions/typescript-language-features/src/features/workspaceSymbols.ts deleted file mode 100644 index 857969be00899..0000000000000 --- a/extensions/typescript-language-features/src/features/workspaceSymbols.ts +++ /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. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as Proto from '../protocol'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import * as fileSchemes from '../utils/fileSchemes'; -import { doesResourceLookLikeAJavaScriptFile, doesResourceLookLikeATypeScriptFile } from '../utils/languageDescription'; -import * as typeConverters from '../utils/typeConverters'; - -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; - default: return vscode.SymbolKind.Variable; - } -} - -class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { - public constructor( - private readonly client: ITypeScriptServiceClient, - private readonly modeIds: string[] - ) { } - - public async provideWorkspaceSymbols( - search: string, - token: vscode.CancellationToken - ): Promise { - const document = this.getDocument(); - if (!document) { - return []; - } - - const filepath = await this.toOpenedFiledPath(document); - if (!filepath) { - return []; - } - - const args: Proto.NavtoRequestArgs = { - file: filepath, - searchValue: search - }; - - const response = await this.client.execute('navto', args, token); - if (response.type !== 'response' || !response.body) { - 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; - } - - private async toOpenedFiledPath(document: vscode.TextDocument) { - if (document.uri.scheme === fileSchemes.git) { - try { - const path = vscode.Uri.file(JSON.parse(document.uri.query)?.path); - if (doesResourceLookLikeATypeScriptFile(path) || doesResourceLookLikeAJavaScriptFile(path)) { - const document = await vscode.workspace.openTextDocument(path); - return this.client.toOpenedFilePath(document); - } - } catch { - // noop - } - } - return this.client.toOpenedFilePath(document); - } - - private static getLabel(item: Proto.NavtoItem) { - const label = item.name; - if (item.kind === 'method' || item.kind === 'function') { - return label + '()'; - } - return label; - } - - private getDocument(): vscode.TextDocument | undefined { - // typescript wants to have a resource even when asking - // general questions so we check the active editor. If this - // doesn't match we take the first TS document. - - const activeDocument = vscode.window.activeTextEditor?.document; - if (activeDocument) { - if (this.modeIds.includes(activeDocument.languageId)) { - return activeDocument; - } - } - - const documents = vscode.workspace.textDocuments; - for (const document of documents) { - if (this.modeIds.includes(document.languageId)) { - return document; - } - } - return undefined; - } -} - -export function register( - client: ITypeScriptServiceClient, - modeIds: string[], -) { - return vscode.languages.registerWorkspaceSymbolProvider(new TypeScriptWorkspaceSymbolProvider(client, modeIds)); -} diff --git a/extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts b/extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts new file mode 100644 index 0000000000000..836f372f2ffe4 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/callHierarchy.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +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 + ) { } + + public async prepareCallHierarchy( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken + ): Promise { + const filepath = this.client.toOpenedFilePath(document); + if (!filepath) { + return undefined; + } + + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + const response = await this.client.execute('prepareCallHierarchy', args, token); + if (response.type !== 'response' || !response.body) { + return undefined; + } + + return Array.isArray(response.body) + ? response.body.map(fromProtocolCallHierarchyItem) + : fromProtocolCallHierarchyItem(response.body); + } + + public async provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + const filepath = this.client.toPath(item.uri); + if (!filepath) { + return undefined; + } + + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, item.selectionRange.start); + const response = await this.client.execute('provideCallHierarchyIncomingCalls', args, token); + if (response.type !== 'response' || !response.body) { + return undefined; + } + + return response.body.map(fromProtocolCallHierchyIncomingCall); + } + + public async provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): Promise { + const filepath = this.client.toPath(item.uri); + if (!filepath) { + return undefined; + } + + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, item.selectionRange.start); + const response = await this.client.execute('provideCallHierarchyOutgoingCalls', args, token); + if (response.type !== 'response' || !response.body) { + return undefined; + } + + return response.body.map(fromProtocolCallHierchyOutgoingCall); + } +} + +function isSourceFileItem(item: Proto.CallHierarchyItem) { + 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)) : item.containerName ?? ''; + const result = new vscode.CallHierarchyItem( + typeConverters.SymbolKind.fromProtocolScriptElementKind(item.kind), + name, + detail, + vscode.Uri.file(item.file), + 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 { + return new vscode.CallHierarchyIncomingCall( + fromProtocolCallHierarchyItem(item.from), + item.fromSpans.map(typeConverters.Range.fromTextSpan) + ); +} + +function fromProtocolCallHierchyOutgoingCall(item: Proto.CallHierarchyOutgoingCall): vscode.CallHierarchyOutgoingCall { + return new vscode.CallHierarchyOutgoingCall( + fromProtocolCallHierarchyItem(item.to), + item.fromSpans.map(typeConverters.Range.fromTextSpan) + ); +} + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient +) { + 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 d5f2f0538d07e..7f0e3e5f0972f 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 76% rename from extensions/typescript-language-features/src/features/implementationsCodeLens.ts rename to extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts index eccb5433e7668..a340e21bed3d5 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(); @@ -24,7 +25,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip const codeLens = inputCodeLens as ReferencesCodeLens; const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); - const response = await this.client.execute('implementation', args, token, { lowPriority: true }); + const response = await this.client.execute('implementation', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document }); if (response.type !== 'response' || !response.body) { codeLens.command = response.type === 'cancelled' ? TypeScriptBaseCodeLensProvider.cancelledCommand @@ -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, ) { - 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/languageFeatures/codeLens/referencesCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts new file mode 100644 index 0000000000000..8fe118de5d2ff --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * 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(); + +export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvider { + public constructor( + protected client: ITypeScriptServiceClient, + protected _cachedResponse: CachedResponse, + private modeId: string + ) { + super(client, _cachedResponse); + } + + public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise { + 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, + executionTarget: ExectuionTarget.Semantic, + cancelOnResourceChange: codeLens.document, + }); + if (response.type !== 'response' || !response.body) { + codeLens.command = response.type === 'cancelled' + ? TypeScriptBaseCodeLensProvider.cancelledCommand + : TypeScriptBaseCodeLensProvider.errorCommand; + return codeLens; + } + + const locations = response.body.refs + .map(reference => + typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)) + .filter(location => + // Exclude original definition from references + !(location.uri.toString() === codeLens.document.toString() && + location.range.start.isEqual(codeLens.range.start))); + + codeLens.command = { + title: this.getCodeLensLabel(locations), + command: locations.length ? 'editor.action.showReferences' : '', + arguments: [codeLens.document, codeLens.range.start, locations] + }; + return codeLens; + } + + private getCodeLensLabel(locations: ReadonlyArray): string { + return locations.length === 1 + ? localize('oneReferenceLabel', '1 reference') + : localize('manyReferenceLabel', '{0} references', locations.length); + } + + protected extractSymbol( + document: vscode.TextDocument, + item: Proto.NavigationTree, + parent: Proto.NavigationTree | null + ): vscode.Range | null { + if (parent && parent.kind === PConst.Kind.enum) { + return getSymbolRange(document, item); + } + + switch (item.kind) { + case PConst.Kind.function: + const showOnAllFunctions = vscode.workspace.getConfiguration(this.modeId).get('referencesCodeLens.showOnAllFunctions'); + if (showOnAllFunctions) { + return getSymbolRange(document, item); + } + // fallthrough + + case PConst.Kind.const: + case PConst.Kind.let: + case PConst.Kind.variable: + // Only show references for exported variables + if (/\bexport\b/.test(item.kindModifiers)) { + return getSymbolRange(document, item); + } + break; + + case PConst.Kind.class: + if (item.text === '') { + break; + } + return getSymbolRange(document, item); + + case PConst.Kind.interface: + case PConst.Kind.type: + case PConst.Kind.enum: + return getSymbolRange(document, item); + + 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: + case PConst.Kind.interface: + case PConst.Kind.type: + return getSymbolRange(document, item); + } + break; + } + + return null; + } +} + +export function register( + selector: DocumentSelector, + modeId: string, + client: ITypeScriptServiceClient, + cachedResponse: CachedResponse, +) { + 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/languageFeatures/completions.ts b/extensions/typescript-language-features/src/languageFeatures/completions.ts new file mode 100644 index 0000000000000..96914bc83a7bb --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/completions.ts @@ -0,0 +1,851 @@ +/*--------------------------------------------------------------------------------------------- + * 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 type * as Proto from '../protocol'; +import * as PConst from '../protocol.const'; +import { ClientCapability, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import API from '../utils/api'; +import { nulToken } from '../utils/cancellation'; +import { applyCodeAction } from '../utils/codeAction'; +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'; +import * as typeConverters from '../utils/typeConverters'; +import TypingsStatus from '../utils/typingsStatus'; +import FileConfigurationManager from './fileConfigurationManager'; + +const localize = nls.loadMessageBundle(); + +interface DotAccessorContext { + readonly range: vscode.Range; + readonly text: string; +} + +interface CompletionContext { + readonly isNewIdentifierLocation: boolean; + readonly isMemberCompletion: boolean; + readonly isInValidCommitCharacterContext: 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, + public readonly tsEntry: Proto.CompletionEntry, + 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 + this.sortText = '\uffff' + tsEntry.sortText; + } else { + this.sortText = tsEntry.sortText; + } + + this.preselect = tsEntry.isRecommended; + this.position = position; + 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; + 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) { + 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 = parseKindModifier(tsEntry.kindModifiers); + if (kindModifiers.has(PConst.KindModifiers.optional)) { + if (!this.insertText) { + this.insertText = this.label; + } + + if (!this.filterText) { + this.filterText = this.label; + } + this.label += '?'; + } + if (kindModifiers.has(PConst.KindModifiers.depreacted)) { + this.tags = [vscode.CompletionItemTag.Deprecated]; + } + + if (kindModifiers.has(PConst.KindModifiers.color)) { + this.kind = vscode.CompletionItemKind.Color; + } + + if (tsEntry.kind === PConst.Kind.script) { + for (const extModifier of PConst.KindModifiers.fileExtensionKindModifiers) { + if (kindModifiers.has(extModifier)) { + if (tsEntry.name.toLowerCase().endsWith(extModifier)) { + this.detail = tsEntry.name; + } else { + this.detail = tsEntry.name + extModifier; + } + break; + } + } + } + } + + this.resolveRange(); + } + + 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 replaceRange = this.getFuzzyWordRange(); + if (replaceRange) { + this.range = { + inserting: new vscode.Range(replaceRange.start, this.position), + replacing: replaceRange + }; + } + } + + 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 { + switch (kind) { + case PConst.Kind.primitiveType: + case PConst.Kind.keyword: + return vscode.CompletionItemKind.Keyword; + + case PConst.Kind.const: + case PConst.Kind.let: + case PConst.Kind.variable: + case PConst.Kind.localVariable: + 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.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; + } + } + + private static getCommitCharacters(context: CompletionContext, entry: Proto.CompletionEntry): string[] | undefined { + if (context.isNewIdentifierLocation || !context.isInValidCommitCharacterContext) { + return undefined; + } + + const commitCharacters: string[] = []; + switch (entry.kind) { + case PConst.Kind.memberGetAccessor: + case PConst.Kind.memberSetAccessor: + case PConst.Kind.constructSignature: + case PConst.Kind.callSignature: + case PConst.Kind.indexSignature: + case PConst.Kind.enum: + case PConst.Kind.interface: + commitCharacters.push('.', ';'); + break; + + case PConst.Kind.module: + case PConst.Kind.alias: + case PConst.Kind.const: + case PConst.Kind.let: + case PConst.Kind.variable: + case PConst.Kind.localVariable: + case PConst.Kind.memberVariable: + case PConst.Kind.class: + case PConst.Kind.function: + case PConst.Kind.method: + case PConst.Kind.keyword: + case PConst.Kind.parameter: + commitCharacters.push('.', ',', ';'); + if (context.enableCallCompletions) { + commitCharacters.push('('); + } + break; + } + return commitCharacters.length === 0 ? undefined : commitCharacters; + } +} + +class CompositeCommand implements Command { + public static readonly ID = '_typescript.composite'; + public readonly id = CompositeCommand.ID; + + public execute(...commands: vscode.Command[]) { + for (const command of commands) { + vscode.commands.executeCommand(command.command, ...(command.arguments || [])); + } + } +} + +class CompletionAcceptedCommand implements Command { + public static readonly ID = '_typescript.onCompletionAccepted'; + public readonly id = CompletionAcceptedCommand.ID; + + 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, + }); + } + } +} + +class ApplyCompletionCodeActionCommand implements Command { + public static readonly ID = '_typescript.applyCompletionCodeAction'; + public readonly id = ApplyCompletionCodeActionCommand.ID; + + public constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + public async execute(_file: string, codeActions: Proto.CodeAction[]): Promise { + if (codeActions.length === 0) { + return true; + } + + if (codeActions.length === 1) { + return applyCodeAction(this.client, codeActions[0], nulToken); + } + + interface MyQuickPickItem extends vscode.QuickPickItem { + index: number; + } + + const selection = await vscode.window.showQuickPick( + codeActions.map((action, i): MyQuickPickItem => ({ + label: action.description, + description: '', + index: i + })), { + placeHolder: localize('selectCodeAction', 'Select code action to apply') + } + ); + + if (!selection) { + return false; + } + + const action = codeActions[selection.index]; + if (!action) { + return false; + } + return applyCodeAction(this.client, action, nulToken); + } +} + +interface CompletionConfiguration { + readonly useCodeSnippetsOnMethodSuggest: boolean; + readonly nameSuggestions: boolean; + readonly pathSuggestions: boolean; + readonly autoImportSuggestions: boolean; +} + +namespace CompletionConfiguration { + export const useCodeSnippetsOnMethodSuggest = 'suggest.completeFunctionCalls'; + export const nameSuggestions = 'suggest.names'; + export const pathSuggestions = 'suggest.paths'; + export const autoImportSuggestions = 'suggest.autoImports'; + + export function getConfigurationForResource( + modeId: string, + resource: vscode.Uri + ): CompletionConfiguration { + const config = vscode.workspace.getConfiguration(modeId, resource); + return { + useCodeSnippetsOnMethodSuggest: config.get(CompletionConfiguration.useCodeSnippetsOnMethodSuggest, false), + pathSuggestions: config.get(CompletionConfiguration.pathSuggestions, true), + autoImportSuggestions: config.get(CompletionConfiguration.autoImportSuggestions, true), + nameSuggestions: config.get(CompletionConfiguration.nameSuggestions, true), + }; + } +} + +class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider { + + public static readonly triggerCharacters = ['.', '"', '\'', '`', '/', '@', '<', '#']; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly modeId: string, + private readonly typingsStatus: TypingsStatus, + private readonly fileConfigurationManager: FileConfigurationManager, + commandManager: CommandManager, + private readonly telemetryReporter: TelemetryReporter, + onCompletionAccepted: (item: vscode.CompletionItem) => void + ) { + commandManager.register(new ApplyCompletionCodeActionCommand(this.client)); + commandManager.register(new CompositeCommand()); + commandManager.register(new CompletionAcceptedCommand(onCompletionAccepted, this.telemetryReporter)); + } + + public async provideCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + context: vscode.CompletionContext + ): Promise | null> { + if (this.typingsStatus.isAcquiringTypings) { + return Promise.reject>({ + label: localize( + { key: 'acquiringTypingsLabel', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] }, + 'Acquiring typings...'), + detail: localize( + { key: 'acquiringTypingsDetail', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] }, + 'Acquiring typings definitions for IntelliSense.') + }); + } + + const file = this.client.toOpenedFilePath(document); + if (!file) { + return null; + } + + const line = document.lineAt(position.line); + const completionConfiguration = CompletionConfiguration.getConfigurationForResource(this.modeId, document.uri); + + if (!this.shouldTrigger(context, line, position)) { + return null; + } + + const wordRange = document.getWordRangeAtPosition(position); + + await this.client.interruptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); + + const args: Proto.CompletionsRequestArgs = { + ...typeConverters.Position.toFileLocationRequestArgs(file, position), + includeExternalModuleExports: completionConfiguration.autoImportSuggestions, + includeInsertTextCompletions: true, + triggerCharacter: this.getTsTriggerCharacter(context), + }; + + let isNewIdentifierLocation = true; + let isIncomplete = false; + let isMemberCompletion = false; + let dotAccessorContext: DotAccessorContext | undefined; + let entries: ReadonlyArray; + let metadata: any | undefined; + let response: ServerResponse.Response | undefined; + let duration: number | undefined; + if (this.client.apiVersion.gte(API.v300)) { + const startTime = Date.now(); + try { + response = await this.client.interruptGetErr(() => this.client.execute('completionInfo', args, token)); + } finally { + duration = Date.now() - startTime; + } + + if (response.type !== 'response' || !response.body) { + this.logCompletionsTelemetry(duration, response); + return null; + } + isNewIdentifierLocation = response.body.isNewIdentifierLocation; + isMemberCompletion = response.body.isMemberCompletion; + if (isMemberCompletion) { + const dotMatch = line.text.slice(0, position.character).match(/\??\.\s*$/) || undefined; + if (dotMatch) { + const range = new vscode.Range(position.translate({ characterDelta: -dotMatch[0].length }), position); + const text = document.getText(range); + dotAccessorContext = { range, text }; + } + } + isIncomplete = (response as any).metadata && (response as any).metadata.isIncomplete; + entries = response.body.entries; + metadata = response.metadata; + } else { + const response = await this.client.interruptGetErr(() => this.client.execute('completions', args, token)); + if (response.type !== 'response' || !response.body) { + return null; + } + + entries = response.body; + metadata = response.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 | 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 { + 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; + } + + return undefined; + } + + public async resolveCompletionItem( + item: MyCompletionItem, + token: vscode.CancellationToken + ): Promise { + const filepath = this.client.toOpenedFilePath(item.document); + if (!filepath) { + return undefined; + } + + const args: Proto.CompletionDetailsRequestArgs = { + ...typeConverters.Position.toFileLocationRequestArgs(filepath, item.position), + entryNames: [ + item.tsEntry.source ? { name: item.tsEntry.name, source: item.tsEntry.source } : item.tsEntry.name + ] + }; + + const response = await this.client.interruptGetErr(() => this.client.execute('completionEntryDetails', args, token)); + if (response.type !== 'response' || !response.body || !response.body.length) { + return item; + } + + const detail = response.body[0]; + + if (!item.detail && detail.displayParts.length) { + item.detail = Previewer.plain(detail.displayParts); + } + item.documentation = this.getDocumentation(detail, item); + + const codeAction = this.getCodeActions(detail, filepath); + const commands: vscode.Command[] = [{ + command: CompletionAcceptedCommand.ID, + title: '', + arguments: [item] + }]; + if (codeAction.command) { + commands.push(codeAction.command); + } + item.additionalTextEdits = codeAction.additionalTextEdits; + + if (item.useCodeSnippet) { + const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, item.document, token); + if (shouldCompleteFunction) { + const { snippet, parameterCount } = snippetForFunctionCall(item, detail.displayParts); + item.insertText = snippet; + if (parameterCount > 0) { + //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' }); + } + } + } + } + + if (commands.length) { + if (commands.length === 1) { + item.command = commands[0]; + } else { + item.command = { + command: CompositeCommand.ID, + title: '', + arguments: commands + }; + } + } + + return item; + } + + private getCodeActions( + detail: Proto.CompletionEntryDetails, + filepath: string + ): { command?: vscode.Command, additionalTextEdits?: vscode.TextEdit[] } { + if (!detail.codeActions || !detail.codeActions.length) { + return {}; + } + + // Try to extract out the additionalTextEdits for the current file. + // Also check if we still have to apply other workspace edits and commands + // using a vscode command + const additionalTextEdits: vscode.TextEdit[] = []; + let hasReaminingCommandsOrEdits = false; + for (const tsAction of detail.codeActions) { + if (tsAction.commands) { + hasReaminingCommandsOrEdits = true; + } + + // Apply all edits in the current file using `additionalTextEdits` + if (tsAction.changes) { + for (const change of tsAction.changes) { + if (change.fileName === filepath) { + additionalTextEdits.push(...change.textChanges.map(typeConverters.TextEdit.fromCodeEdit)); + } else { + hasReaminingCommandsOrEdits = true; + } + } + } + } + + let command: vscode.Command | undefined = undefined; + if (hasReaminingCommandsOrEdits) { + // Create command that applies all edits not in the current file. + command = { + title: '', + command: ApplyCompletionCodeActionCommand.ID, + arguments: [filepath, detail.codeActions.map((x): Proto.CodeAction => ({ + commands: x.commands, + description: x.description, + changes: x.changes.filter(x => x.fileName !== filepath) + }))] + }; + } + + return { + command, + additionalTextEdits: additionalTextEdits.length ? additionalTextEdits : undefined + }; + } + + private isInValidCommitCharacterContext( + document: vscode.TextDocument, + position: vscode.Position + ): boolean { + if (this.client.apiVersion.lt(API.v320)) { + // 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) { + const preText = document.getText(new vscode.Range( + position.line, 0, + position.line, position.character)); + return preText.match(/(\s|^)\.$/ig) === null; + } + } + + return true; + } + + private shouldTrigger( + context: vscode.CompletionContext, + line: vscode.TextLine, + position: vscode.Position + ): boolean { + if (context.triggerCharacter && this.client.apiVersion.lt(API.v290)) { + if ((context.triggerCharacter === '"' || context.triggerCharacter === '\'')) { + // make sure we are in something that looks like the start of an import + const pre = line.text.slice(0, position.character); + if (!pre.match(/\b(from|import)\s*["']$/) && !pre.match(/\b(import|require)\(['"]$/)) { + return false; + } + } + + if (context.triggerCharacter === '/') { + // make sure we are in something that looks like an import path + const pre = line.text.slice(0, position.character); + if (!pre.match(/\b(from|import)\s*["'][^'"]*$/) && !pre.match(/\b(import|require)\(['"][^'"]*$/)) { + return false; + } + } + + if (context.triggerCharacter === '@') { + // make sure we are in something that looks like the start of a jsdoc comment + const pre = line.text.slice(0, position.character); + if (!pre.match(/^\s*\*[ ]?@/) && !pre.match(/\/\*\*+[ ]?@/)) { + return false; + } + } + + if (context.triggerCharacter === '<') { + return false; + } + } + + return true; + } + + private getDocumentation( + detail: Proto.CompletionEntryDetails, + item: MyCompletionItem + ): vscode.MarkdownString | undefined { + const documentation = new vscode.MarkdownString(); + if (detail.source) { + const importPath = `'${Previewer.plain(detail.source)}'`; + const autoImportLabel = localize('autoImportLabel', 'Auto import from {0}', importPath); + item.detail = `${autoImportLabel}\n${item.detail}`; + } + Previewer.addMarkdownDocumentation(documentation, detail.documentation, detail.tags); + + return documentation.value.length ? documentation : undefined; + } + + private async isValidFunctionCompletionContext( + filepath: string, + position: vscode.Position, + document: vscode.TextDocument, + token: vscode.CancellationToken + ): Promise { + // 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); + const response = await this.client.execute('quickinfo', args, token); + if (response.type === 'response' && response.body) { + switch (response.body.kind) { + case 'var': + case 'let': + case 'const': + case 'alias': + return false; + } + } + } catch { + // Noop + } + + // Don't complete function call if there is already something that looks like a function call + // 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; + } +} + +function shouldExcludeCompletionEntry( + element: Proto.CompletionEntry, + completionConfiguration: CompletionConfiguration +) { + return ( + (!completionConfiguration.nameSuggestions && element.kind === PConst.Kind.warning) + || (!completionConfiguration.pathSuggestions && + (element.kind === PConst.Kind.directory || element.kind === PConst.Kind.script || element.kind === PConst.Kind.externalModuleName)) + || (!completionConfiguration.autoImportSuggestions && element.hasAction) + ); +} + +export function register( + selector: DocumentSelector, + modeId: string, + client: ITypeScriptServiceClient, + typingsStatus: TypingsStatus, + fileConfigurationManager: FileConfigurationManager, + commandManager: CommandManager, + telemetryReporter: TelemetryReporter, + onCompletionAccepted: (item: vscode.CompletionItem) => void +) { + 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); + }); +} 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 fd3a1f10e79e6..6d6e4fc2851f3 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 327573e8c1859..25a9591cc5687 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(); + private readonly _diagnostics: ResourceMap; private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; - private readonly _pendingUpdates = new ResourceMap(); + private readonly _pendingUpdates: ResourceMap; private readonly _updateDelay = 50; constructor( - owner: string + owner: string, + onCaseInsenitiveFileSystem: boolean ) { super(); + this._diagnostics = new ResourceMap(undefined, { onCaseInsenitiveFileSystem }); + this._pendingUpdates = new ResourceMap(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 c0a222e47df63..2b0f683e58945 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 970a265f218ad..3a7b43ed5eb55 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 75% rename from extensions/typescript-language-features/src/features/documentSymbol.ts rename to extensions/typescript-language-features/src/languageFeatures/documentSymbol.ts index abcd43238a12b..d0b8b9ad9019c 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 { CachedResponse } from '../tsServer/cachedResponse'; import { ITypeScriptServiceClient } from '../typescriptService'; +import { DocumentSelector } from '../utils/documentSelector'; +import { parseKindModifier } from '../utils/modifiers'; import * as typeConverters from '../utils/typeConverters'; -import { CachedResponse } from '../tsServer/cachedResponse'; 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, @@ -45,33 +48,44 @@ 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); + range.contains(selectionRange) ? selectionRange : range); + + + const kindModifiers = parseKindModifier(item.kindModifiers); + if (kindModifiers.has(PConst.KindModifiers.depreacted)) { + symbolInfo.tags = [vscode.SymbolTag.Deprecated]; + } for (const child of children) { if (child.spans.some(span => !!range.intersection(typeConverters.Range.fromTextSpan(span)))) { @@ -82,7 +96,7 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider } if (shouldInclude) { - bucket.push(symbolInfo); + output.push(symbolInfo); } } @@ -97,12 +111,11 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider } } - export function register( - selector: vscode.DocumentSelector, + selector: DocumentSelector, client: ITypeScriptServiceClient, cachedResponse: CachedResponse, ) { - 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 80% rename from extensions/typescript-language-features/src/features/fileConfigurationManager.ts rename to extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index ff9245b4a5e17..dbd3513f0d4ff 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -4,22 +4,20 @@ *--------------------------------------------------------------------------------------------*/ 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 { 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'; -import { Disposable } from '../utils/dispose'; - -function objsAreEqual(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; } - return true; } interface FileConfiguration { @@ -28,19 +26,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>(); + private readonly formatOptions: ResourceMap>; 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 @@ -144,9 +141,7 @@ export default class FileConfigurationManager extends Disposable { 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, @@ -169,8 +164,6 @@ export default class FileConfigurationManager extends Disposable { placeOpenBraceOnNewLineForControlBlocks: config.get('placeOpenBraceOnNewLineForControlBlocks'), semicolons: config.get('semicolons'), }; - - return settings; } private getPreferences(document: vscode.TextDocument): Proto.UserPreferences { @@ -179,16 +172,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('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('renameShorthandProperties', true) === false ? false : preferencesConfig.get('useAliasesForRenames', true), allowRenameOfImportPath: true, + includeAutomaticOptionalChainCompletions: config.get('suggest.includeAutomaticOptionalChainCompletions', true), + provideRefactorNotApplicableReason: true, }; + + return preferences; } private getQuoteStylePreference(config: vscode.WorkspaceConfiguration) { @@ -207,3 +209,12 @@ function getImportModuleSpecifierPreference(config: vscode.WorkspaceConfiguratio default: return undefined; } } + +function getImportModuleSpecifierEndingPreference(config: vscode.WorkspaceConfiguration) { + switch (config.get('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 0000000000000..6a43c535a8bbd --- /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; + readonly fixName: string; +} + +async function buildIndividualFixes( + fixes: readonly AutoFix[], + edit: vscode.WorkspaceEdit, + client: ITypeScriptServiceClient, + file: string, + diagnostics: readonly vscode.Diagnostic[], + token: vscode.CancellationToken, +): Promise { + 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 { + 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 async build( + client: ITypeScriptServiceClient, + file: string, + diagnostics: readonly vscode.Diagnostic[], + token: vscode.CancellationToken, + ): Promise; +} + +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 { + 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 { + 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 { + 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 { + 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/languageFeatures/folding.ts b/extensions/typescript-language-features/src/languageFeatures/folding.ts new file mode 100644 index 0000000000000..5b18decb440b8 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/folding.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. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import type * as Proto from '../protocol'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { coalesce } from '../utils/arrays'; +import { conditionalRegistration, requireMinVersion } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import * as typeConverters from '../utils/typeConverters'; + +class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { + public static readonly minVersion = API.v280; + + public constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + async provideFoldingRanges( + document: vscode.TextDocument, + _context: vscode.FoldingContext, + token: vscode.CancellationToken + ): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return; + } + + const args: Proto.FileRequestArgs = { file }; + const response = await this.client.execute('getOutliningSpans', args, token); + if (response.type !== 'response' || !response.body) { + return; + } + + return coalesce(response.body.map(span => this.convertOutliningSpan(span, document))); + } + + private convertOutliningSpan( + span: Proto.OutliningSpan, + document: vscode.TextDocument + ): vscode.FoldingRange | undefined { + const range = typeConverters.Range.fromTextSpan(span.textSpan); + const kind = TypeScriptFoldingProvider.getFoldingRangeKind(span); + + // Workaround for #49904 + if (span.kind === 'comment') { + const line = document.lineAt(range.start.line).text; + if (line.match(/\/\/\s*#endregion/gi)) { + return undefined; + } + } + + const start = range.start.line; + // workaround for #47240 + const end = (range.end.character > 0 && ['}', ']'].includes(document.getText(new vscode.Range(range.end.translate(0, -1), range.end)))) + ? Math.max(range.end.line - 1, range.start.line) + : range.end.line; + + return new vscode.FoldingRange(start, end, kind); + } + + private static getFoldingRangeKind(span: Proto.OutliningSpan): vscode.FoldingRangeKind | undefined { + switch (span.kind) { + case 'comment': return vscode.FoldingRangeKind.Comment; + case 'region': return vscode.FoldingRangeKind.Region; + case 'imports': return vscode.FoldingRangeKind.Imports; + case 'code': + default: return undefined; + } + } +} + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient, +): vscode.Disposable { + return conditionalRegistration([ + requireMinVersion(client, TypeScriptFoldingProvider.minVersion), + ], () => { + return vscode.languages.registerFoldingRangeProvider(selector.syntax, + new TypeScriptFoldingProvider(client)); + }); +} diff --git a/extensions/typescript-language-features/src/languageFeatures/formatting.ts b/extensions/typescript-language-features/src/languageFeatures/formatting.ts new file mode 100644 index 0000000000000..cc2469774a53f --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/formatting.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ITypeScriptServiceClient } from '../typescriptService'; +import { conditionalRegistration, requireConfiguration } from '../utils/dependentRegistration'; +import { DocumentSelector } from '../utils/documentSelector'; +import * as typeConverters from '../utils/typeConverters'; +import FileConfigurationManager from './fileConfigurationManager'; + +class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEditProvider, vscode.OnTypeFormattingEditProvider { + public constructor( + private readonly client: ITypeScriptServiceClient, + private readonly formattingOptionsManager: FileConfigurationManager + ) { } + + public async provideDocumentRangeFormattingEdits( + document: vscode.TextDocument, + range: vscode.Range, + options: vscode.FormattingOptions, + token: vscode.CancellationToken + ): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return undefined; + } + + await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); + + const args = typeConverters.Range.toFormattingRequestArgs(file, range); + const response = await this.client.execute('format', args, token); + if (response.type !== 'response' || !response.body) { + return undefined; + } + + return response.body.map(typeConverters.TextEdit.fromCodeEdit); + } + + public async provideOnTypeFormattingEdits( + document: vscode.TextDocument, + position: vscode.Position, + ch: string, + options: vscode.FormattingOptions, + token: vscode.CancellationToken + ): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return []; + } + + await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); + + const args: Proto.FormatOnKeyRequestArgs = { + ...typeConverters.Position.toFileLocationRequestArgs(file, position), + key: ch + }; + const response = await this.client.execute('formatonkey', args, token); + if (response.type !== 'response' || !response.body) { + return []; + } + + const result: vscode.TextEdit[] = []; + 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. + // 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 === '') { + const lText = document.lineAt(range.start.line).text; + // If the edit leaves something on the line keep the edit (note that the end character is exclusive). + // Keep it also if it removes something else than whitespace + if (lText.trim().length > 0 || lText.length > range.end.character) { + result.push(textEdit); + } + } else { + result.push(textEdit); + } + } + return result; + } +} + +export function register( + selector: DocumentSelector, + modeId: string, + client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager +) { + return conditionalRegistration([ + requireConfiguration(modeId, 'format.enable'), + ], () => { + const formattingProvider = new TypeScriptFormattingProvider(client, fileConfigurationManager); + return vscode.Disposable.from( + 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 0000000000000..a4de074897f7f --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/hover.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +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 { + 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(response.body, response._serverType), + typeConverters.Range.fromTextSpan(response.body)); + } + + private getContents( + data: Proto.QuickInfoResponseBody, + source: ServerType | undefined, + ) { + const parts: vscode.MarkedString[] = []; + + if (data.displayString) { + const displayParts: string[] = []; + + if (source === ServerType.Syntax && this.client.capabilities.has(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/languageFeatures/implementations.ts b/extensions/typescript-language-features/src/languageFeatures/implementations.ts new file mode 100644 index 0000000000000..bf3ddfee414bb --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/implementations.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 vscode from 'vscode'; +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 { + public provideImplementation(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { + return this.getSymbolLocations('implementation', document, position, token); + } +} + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient, +) { + 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 89% rename from extensions/typescript-language-features/src/features/jsDocCompletions.ts rename to extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts index 82aacc8927a1d..6e091af1692c6 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'; @@ -27,9 +28,8 @@ class JsDocCompletionItem extends vscode.CompletionItem { const prefix = line.slice(0, position.character).match(/\/\**\s*$/); const suffix = line.slice(position.character).match(/^\s*\**\//); const start = position.translate(0, prefix ? -prefix[0].length : 0); - this.range = new vscode.Range( - start, - position.translate(0, suffix ? suffix[0].length : 0)); + const range = new vscode.Range(start, position.translate(0, suffix ? suffix[0].length : 0)); + this.range = { inserting: range, replacing: range }; } } @@ -111,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 95% rename from extensions/typescript-language-features/src/features/languageConfiguration.ts rename to extensions/typescript-language-features/src/languageFeatures/languageConfiguration.ts index 59fefa1007b7d..e7f0af0ff9c92 100644 --- a/extensions/typescript-language-features/src/features/languageConfiguration.ts +++ b/extensions/typescript-language-features/src/languageFeatures/languageConfiguration.ts @@ -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 82% rename from extensions/typescript-language-features/src/features/organizeImports.ts rename to extensions/typescript-language-features/src/languageFeatures/organizeImports.ts index bb7880800a609..218cda4103f6c 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/languageFeatures/quickFix.ts b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts new file mode 100644 index 0000000000000..0965a574721d3 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/quickFix.ts @@ -0,0 +1,421 @@ +/*--------------------------------------------------------------------------------------------- + * 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 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 { 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'; +import FileConfigurationManager from './fileConfigurationManager'; + +const localize = nls.loadMessageBundle(); + +class ApplyCodeActionCommand implements Command { + public static readonly ID = '_typescript.applyCodeActionCommand'; + public readonly id = ApplyCodeActionCommand.ID; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly telemetryReporter: TelemetryReporter, + ) { } + + public async execute( + action: Proto.CodeFixAction + ): Promise { + /* __GDPR__ + "quickFix.execute" : { + "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('quickFix.execute', { + fixName: action.fixName + }); + + return applyCodeActionCommands(this.client, action.commands, nulToken); + } +} + + +class ApplyFixAllCodeAction implements Command { + public static readonly ID = '_typescript.applyFixAllCodeAction'; + public readonly id = ApplyFixAllCodeAction.ID; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly telemetryReporter: TelemetryReporter, + ) { } + + public async execute( + file: string, + tsAction: Proto.CodeFixAction, + ): Promise { + if (!tsAction.fixId) { + return; + } + + /* __GDPR__ + "quickFixAll.execute" : { + "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('quickFixAll.execute', { + fixName: tsAction.fixName + }); + + const args: Proto.GetCombinedCodeFixRequestArgs = { + scope: { + type: 'file', + args: { file } + }, + fixId: tsAction.fixId, + }; + + const response = await this.client.execute('getCombinedCodeFix', args, nulToken); + if (response.type !== 'response' || !response.body) { + return undefined; + } + + const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, response.body.changes); + await vscode.workspace.applyEdit(edit); + await applyCodeActionCommands(this.client, response.body.commands, nulToken); + } +} + +/** + * Unique set of diagnostics keyed on diagnostic range and error code. + */ +class DiagnosticsSet { + public static from(diagnostics: vscode.Diagnostic[]) { + const values = new Map(); + for (const diagnostic of diagnostics) { + values.set(DiagnosticsSet.key(diagnostic), diagnostic); + } + return new DiagnosticsSet(values); + } + + private static key(diagnostic: vscode.Diagnostic) { + const { start, end } = diagnostic.range; + return `${diagnostic.code}-${start.line},${start.character}-${end.line},${end.character}`; + } + + private constructor( + private readonly _values: Map + ) { } + + public get values(): Iterable { + return this._values.values(); + } + + public get size() { + return this._values.size; + } +} + +class VsCodeCodeAction extends vscode.CodeAction { + constructor( + public readonly tsAction: Proto.CodeFixAction, + title: string, + kind: vscode.CodeActionKind, + public readonly isFixAll: boolean, + ) { + super(title, kind); + } +} + +class CodeActionSet { + private readonly _actions = new Set(); + private readonly _fixAllActions = new Map<{}, VsCodeCodeAction>(); + + public get values(): Iterable { + return this._actions; + } + + 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: VsCodeCodeAction) { + const existing = this._fixAllActions.get(fixId); + if (existing) { + // reinsert action at back of actions list + this._actions.delete(existing); + } + this.addAction(action); + this._fixAllActions.set(fixId, action); + } + + public hasFixAllAction(fixId: {}) { + return this._fixAllActions.has(fixId); + } +} + +class SupportedCodeActionProvider { + public constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + public async getFixableDiagnosticsForContext(context: vscode.CodeActionContext): Promise { + const fixableCodes = await this.fixableDiagnosticCodes; + return DiagnosticsSet.from( + context.diagnostics.filter(diagnostic => typeof diagnostic.code !== 'undefined' && fixableCodes.has(diagnostic.code + ''))); + } + + @memoize + private get fixableDiagnosticCodes(): Thenable> { + return this.client.execute('getSupportedCodeFixes', null, nulToken) + .then(response => response.type === 'response' ? response.body || [] : []) + .then(codes => new Set(codes)); + } +} + +class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { + + public static readonly metadata: vscode.CodeActionProviderMetadata = { + providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] + }; + + private readonly supportedCodeActionProvider: SupportedCodeActionProvider; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly formattingConfigurationManager: FileConfigurationManager, + commandManager: CommandManager, + private readonly diagnosticsManager: DiagnosticsManager, + telemetryReporter: TelemetryReporter + ) { + commandManager.register(new ApplyCodeActionCommand(client, telemetryReporter)); + commandManager.register(new ApplyFixAllCodeAction(client, telemetryReporter)); + + this.supportedCodeActionProvider = new SupportedCodeActionProvider(client); + } + + public async provideCodeActions( + document: vscode.TextDocument, + _range: vscode.Range, + context: vscode.CodeActionContext, + token: vscode.CancellationToken + ): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return []; + } + + const fixableDiagnostics = await this.supportedCodeActionProvider.getFixableDiagnosticsForContext(context); + if (!fixableDiagnostics.size) { + return []; + } + + if (this.client.bufferSyncSupport.hasPendingDiagnostics(document.uri)) { + return []; + } + + await this.formattingConfigurationManager.ensureConfigurationForDocument(document, token); + + const results = new CodeActionSet(); + for (const diagnostic of fixableDiagnostics.values) { + await this.getFixesForDiagnostic(document, file, diagnostic, results, token); + } + + const allActions = Array.from(results.values); + for (const action of allActions) { + action.isPreferred = isPreferredFix(action, allActions); + } + return allActions; + } + + private async getFixesForDiagnostic( + document: vscode.TextDocument, + file: string, + diagnostic: vscode.Diagnostic, + results: CodeActionSet, + token: vscode.CancellationToken, + ): Promise { + 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) { + return results; + } + + for (const tsCodeFix of response.body) { + this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix as Proto.CodeFixAction); + } + return results; + } + + private addAllFixesForTsCodeAction( + results: CodeActionSet, + document: vscode.TextDocument, + file: string, + diagnostic: vscode.Diagnostic, + tsAction: Proto.CodeFixAction + ): CodeActionSet { + results.addAction(this.getSingleFixForTsCodeAction(diagnostic, tsAction)); + this.addFixAllForTsCodeAction(results, document, file, diagnostic, tsAction as Proto.CodeFixAction); + return results; + } + + private getSingleFixForTsCodeAction( + diagnostic: vscode.Diagnostic, + tsAction: Proto.CodeFixAction + ): VsCodeCodeAction { + const codeAction = new VsCodeCodeAction(tsAction, tsAction.description, vscode.CodeActionKind.QuickFix, false); + codeAction.edit = getEditForCodeAction(this.client, tsAction); + codeAction.diagnostics = [diagnostic]; + codeAction.command = { + command: ApplyCodeActionCommand.ID, + arguments: [tsAction], + title: '' + }; + return codeAction; + } + + private addFixAllForTsCodeAction( + results: CodeActionSet, + document: vscode.TextDocument, + file: string, + diagnostic: vscode.Diagnostic, + tsAction: Proto.CodeFixAction, + ): CodeActionSet { + if (!tsAction.fixId || this.client.apiVersion.lt(API.v270) || results.hasFixAllAction(tsAction.fixId)) { + return results; + } + + // Make sure there are multiple diagnostics of the same type in the file + if (!this.diagnosticsManager.getDiagnostics(document.uri).some(x => { + if (x === diagnostic) { + return false; + } + return x.code === diagnostic.code + || (fixAllErrorCodes.has(x.code as number) && fixAllErrorCodes.get(x.code as number) === fixAllErrorCodes.get(diagnostic.code as number)); + })) { + return results; + } + + const action = new VsCodeCodeAction( + tsAction, + tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), + vscode.CodeActionKind.QuickFix, true); + action.diagnostics = [diagnostic]; + action.command = { + command: ApplyFixAllCodeAction.ID, + arguments: [file, tsAction], + title: '' + }; + results.addFixAllAction(tsAction.fixId, action); + return results; + } +} + +// Some fix all actions can actually fix multiple differnt diagnostics. Make sure we still show the fix all action +// in such cases +const fixAllErrorCodes = new Map([ + // Missing async + [2339, 2339], + [2345, 2339], +]); + +const preferredFixes = new Map([ + [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( + 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: DocumentSelector, + client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, + commandManager: CommandManager, + diagnosticsManager: DiagnosticsManager, + telemetryReporter: TelemetryReporter +) { + 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/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts new file mode 100644 index 0000000000000..edfebfcb56b0b --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -0,0 +1,452 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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 { 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'; + +const localize = nls.loadMessageBundle(); + + +namespace Experimental { + export interface RefactorActionInfo extends Proto.RefactorActionInfo { + readonly notApplicableReason?: string; + } + + export type RefactorTriggerReason = 'implicit' | 'invoked'; + + export interface GetApplicableRefactorsRequestArgs extends Proto.FileRangeRequestArgs { + readonly triggerReason?: RefactorTriggerReason; + } +} + + +class ApplyRefactoringCommand implements Command { + public static readonly ID = '_typescript.applyRefactoring'; + public readonly id = ApplyRefactoringCommand.ID; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly telemetryReporter: TelemetryReporter + ) { } + + public async execute( + document: vscode.TextDocument, + refactor: string, + action: string, + range: vscode.Range + ): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return false; + } + + /* __GDPR__ + "refactor.execute" : { + "action" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('refactor.execute', { + action: 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) { + vscode.window.showErrorMessage(localize('refactoringFailed', "Could not apply refactoring")); + return false; + } + + const workspaceEdit = await this.toWorkspaceEdit(response.body); + if (!(await vscode.workspace.applyEdit(workspaceEdit))) { + return false; + } + + const renameLocation = response.body.renameLocation; + if (renameLocation) { + await vscode.commands.executeCommand('editor.action.rename', [ + 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; + } +} + +class SelectRefactorCommand implements Command { + public static readonly ID = '_typescript.selectRefactoring'; + public readonly id = SelectRefactorCommand.ID; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly doRefactoring: ApplyRefactoringCommand + ) { } + + public async execute( + document: vscode.TextDocument, + info: Proto.ApplicableRefactorInfo, + range: vscode.Range + ): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return false; + } + const selected = await vscode.window.showQuickPick(info.actions.map((action): vscode.QuickPickItem => ({ + label: action.name, + description: action.description, + }))); + if (!selected) { + return false; + } + return this.doRefactoring.execute(document, info.name, selected.label, range); + } +} + +interface CodeActionKind { + readonly kind: vscode.CodeActionKind; + matches(refactor: Proto.RefactorActionInfo): boolean; +} + +const Extract_Function = Object.freeze({ + kind: vscode.CodeActionKind.RefactorExtract.append('function'), + matches: refactor => refactor.name.startsWith('function_') +}); + +const Extract_Constant = Object.freeze({ + kind: vscode.CodeActionKind.RefactorExtract.append('constant'), + matches: refactor => refactor.name.startsWith('constant_') +}); + +const Extract_Type = Object.freeze({ + kind: vscode.CodeActionKind.RefactorExtract.append('type'), + matches: refactor => refactor.name.startsWith('Extract to type alias') +}); + +const Extract_Interface = Object.freeze({ + kind: vscode.CodeActionKind.RefactorExtract.append('interface'), + matches: refactor => refactor.name.startsWith('Extract to interface') +}); + +const Move_NewFile = Object.freeze({ + kind: vscode.CodeActionKind.Refactor.append('move').append('newFile'), + matches: refactor => refactor.name.startsWith('Move to a new file') +}); + +const Rewrite_Import = Object.freeze({ + kind: vscode.CodeActionKind.RefactorRewrite.append('import'), + matches: refactor => refactor.name.startsWith('Convert namespace import') || refactor.name.startsWith('Convert named imports') +}); + +const Rewrite_Export = Object.freeze({ + kind: vscode.CodeActionKind.RefactorRewrite.append('export'), + matches: refactor => refactor.name.startsWith('Convert default export') || refactor.name.startsWith('Convert named export') +}); + +const Rewrite_Arrow_Braces = Object.freeze({ + kind: vscode.CodeActionKind.RefactorRewrite.append('arrow').append('braces'), + matches: refactor => refactor.name.startsWith('Convert default export') || refactor.name.startsWith('Convert named export') +}); + +const Rewrite_Parameters_ToDestructured = Object.freeze({ + kind: vscode.CodeActionKind.RefactorRewrite.append('parameters').append('toDestructured'), + matches: refactor => refactor.name.startsWith('Convert parameters to destructured object') +}); + +const Rewrite_Property_GenerateAccessors = Object.freeze({ + kind: vscode.CodeActionKind.RefactorRewrite.append('property').append('generateAccessors'), + matches: refactor => refactor.name.startsWith('Generate \'get\' and \'set\' accessors') +}); + +const allKnownCodeActionKinds = [ + Extract_Function, + Extract_Constant, + Extract_Type, + Extract_Interface, + Move_NewFile, + Rewrite_Import, + Rewrite_Export, + Rewrite_Arrow_Braces, + Rewrite_Parameters_ToDestructured, + Rewrite_Property_GenerateAccessors +]; + +class TypeScriptRefactorProvider implements vscode.CodeActionProvider { + public static readonly minVersion = API.v240; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly formattingOptionsManager: FormattingOptionsManager, + commandManager: CommandManager, + telemetryReporter: TelemetryReporter + ) { + const doRefactoringCommand = commandManager.register(new ApplyRefactoringCommand(this.client, telemetryReporter)); + commandManager.register(new SelectRefactorCommand(this.client, doRefactoringCommand)); + } + + public static readonly metadata: vscode.CodeActionProviderMetadata = { + providedCodeActionKinds: [ + 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( + document: vscode.TextDocument, + rangeOrSelection: vscode.Range | vscode.Selection, + context: vscode.CodeActionContext, + token: vscode.CancellationToken + ): Promise { + if (!this.shouldTrigger(rangeOrSelection, context)) { + return undefined; + } + if (!this.client.toOpenedFilePath(document)) { + return undefined; + } + + const response = await this.client.interruptGetErr(() => { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return undefined; + } + this.formattingOptionsManager.ensureConfigurationForDocument(document, token); + + const args: Experimental.GetApplicableRefactorsRequestArgs = { + ...typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection), + triggerReason: this.toTsTriggerReason(context), + }; + return this.client.execute('getApplicableRefactors', args, token); + }); + if (response?.type !== 'response' || !response.body) { + return undefined; + } + + const actions = this.convertApplicableRefactors(response.body, document, rangeOrSelection); + if (!context.only) { + return actions; + } + return this.pruneInvalidActions(this.appendInvalidActions(actions), context.only, /* numberOfInvalid = */ 5); + } + + private toTsTriggerReason(context: vscode.CodeActionContext): Experimental.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[] = []; + 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] + }; + actions.push(codeAction); + } else { + for (const action of info.actions) { + actions.push(this.refactorActionToCodeAction(action, document, info, rangeOrSelection, info.actions)); + } + } + } + return actions; + } + + private refactorActionToCodeAction( + action: Experimental.RefactorActionInfo, + document: vscode.TextDocument, + info: Proto.ApplicableRefactorInfo, + rangeOrSelection: vscode.Range | vscode.Selection, + allActions: readonly Proto.RefactorActionInfo[], + ) { + const codeAction = new vscode.CodeAction(action.description, TypeScriptRefactorProvider.getKind(action)); + + // https://github.com/microsoft/TypeScript/pull/37871 + if (action.notApplicableReason) { + codeAction.disabled = { reason: action.notApplicableReason }; + } else { + codeAction.command = { + title: action.description, + command: ApplyRefactoringCommand.ID, + arguments: [document, info.name, action.name, rangeOrSelection], + }; + } + + codeAction.isPreferred = TypeScriptRefactorProvider.isPreferred(action, allActions); + return codeAction; + } + + private shouldTrigger(rangeOrSelection: vscode.Range | vscode.Selection, context: vscode.CodeActionContext) { + if (context.only && !vscode.CodeActionKind.Refactor.contains(context.only)) { + return false; + } + + return rangeOrSelection instanceof vscode.Selection; + } + + private static getKind(refactor: Proto.RefactorActionInfo) { + const match = allKnownCodeActionKinds.find(kind => kind.matches(refactor)); + return match ? match.kind : vscode.CodeActionKind.Refactor; + } + + private static isPreferred( + action: Proto.RefactorActionInfo, + allActions: readonly Proto.RefactorActionInfo[], + ): boolean { + if (Extract_Constant.matches(action)) { + // 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; + } + return false; + } + + 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"), + Extract_Constant.kind); + + disabledAction.disabled = { + reason: localize('extractConstant.disabled.reason', "The current selection cannot be extracted"), + }; + disabledAction.isPreferred = true; + + actions.push(disabledAction); + } + + if (!actions.some(action => action.kind && Extract_Function.kind.contains(action.kind))) { + const disabledAction = new vscode.CodeAction( + localize('extractFunction.disabled.title', "Extract to function"), + Extract_Function.kind); + + disabledAction.disabled = { + reason: localize('extractFunction.disabled.reason', "The current selection cannot be extracted"), + }; + actions.push(disabledAction); + } + 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: DocumentSelector, + client: ITypeScriptServiceClient, + formattingOptionsManager: FormattingOptionsManager, + commandManager: CommandManager, + telemetryReporter: TelemetryReporter, +) { + 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/languageFeatures/references.ts b/extensions/typescript-language-features/src/languageFeatures/references.ts new file mode 100644 index 0000000000000..1ee8d1504297b --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/references.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +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 { + public constructor( + private readonly client: ITypeScriptServiceClient) { } + + public async provideReferences( + document: vscode.TextDocument, + position: vscode.Position, + options: vscode.ReferenceContext, + token: vscode.CancellationToken + ): Promise { + const filepath = this.client.toOpenedFilePath(document); + if (!filepath) { + return []; + } + + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + const response = await this.client.execute('references', args, token); + if (response.type !== 'response' || !response.body) { + return []; + } + + const result: vscode.Location[] = []; + for (const ref of response.body.refs) { + if (!options.includeDeclaration && ref.isDefinition) { + continue; + } + const url = this.client.toResource(ref.file); + const location = typeConverters.Location.fromTextSpan(url, ref); + result.push(location); + } + return result; + } +} + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient +) { + 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 aff2c508c5309..e3e1b3c694e46 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 { + 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(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(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(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(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/languageFeatures/semanticTokens.ts b/extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts new file mode 100644 index 0000000000000..c404ac0b95a34 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/semanticTokens.ts @@ -0,0 +1,283 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// all constants are const +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}`); + +// 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( + // register only as a range provider + vscode.languages.registerDocumentRangeSemanticTokensProvider(selector.semantic, provider, provider.getLegend()), + ); + }); +} + +/** + * Prototype of a DocumentSemanticTokensProvider, relying on the experimental `encodedSemanticClassifications-full` request from the TypeScript server. + * As the results retured by the TypeScript server are limited, we also add a Typescript plugin (typescript-vscode-sh-plugin) to enrich the returned token. + * See https://github.com/aeschli/typescript-vscode-sh-plugin. + */ +class DocumentSemanticTokensProvider implements vscode.DocumentSemanticTokensProvider, vscode.DocumentRangeSemanticTokensProvider { + + constructor(private readonly client: ITypeScriptServiceClient) { + } + + getLegend(): vscode.SemanticTokensLegend { + return new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers); + } + + async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file || document.getText().length > CONTENT_LENGTH_LIMIT) { + return null; + } + return this._provideSemanticTokens(document, { file, start: 0, length: document.getText().length }, token); + } + + async provideDocumentRangeSemanticTokens(document: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise { + const file = this.client.toOpenedFilePath(document); + 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); + } + + async _provideSemanticTokens(document: vscode.TextDocument, requestArg: ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs, token: vscode.CancellationToken): Promise { + const file = this.client.toOpenedFilePath(document); + if (!file) { + return null; + } + + let versionBeforeRequest = document.version; + + const response = await (this.client as ExperimentalProtocol.IExtendedTypeScriptServiceClient).execute('encodedSemanticClassifications-full', requestArg, token); + if (response.type !== 'response' || !response.body) { + return null; + } + + const versionAfterRequest = document.version; + + if (versionBeforeRequest !== versionAfterRequest) { + // cannot convert result's offsets to (line;col) values correctly + // a new request will come in soon... + // + // 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; + + const builder = new vscode.SemanticTokensBuilder(); + let i = 0; + while (i < tokenSpan.length) { + const offset = tokenSpan[i++]; + const length = tokenSpan[i++]; + const tsClassification = tokenSpan[i++]; + + let tokenModifiers = 0; + let tokenType = getTokenTypeFromClassification(tsClassification); + if (tokenType !== undefined) { + // it's a classification as returned by the typescript-vscode-sh-plugin + tokenModifiers = getTokenModifierFromClassification(tsClassification); + } else { + // typescript-vscode-sh-plugin is not present + tokenType = tokenTypeMap[tsClassification]; + if (tokenType === undefined) { + continue; + } + } + + // we can use the document's range conversion methods because the result is at the same version as the document + const startPos = document.positionAt(offset); + const endPos = document.positionAt(offset + length); + + for (let line = startPos.line; line <= endPos.line; line++) { + const startCharacter = (line === startPos.line ? startPos.character : 0); + const endCharacter = (line === endPos.line ? endPos.character : document.lineAt(line).text.length); + builder.push(line, startCharacter, endCharacter - startCharacter, tokenType, tokenModifiers); + } + } + return builder.build(); + } +} + +function waitForDocumentChangesToEnd(document: vscode.TextDocument) { + let version = document.version; + return new Promise((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 + +function getTokenTypeFromClassification(tsClassification: number): number | undefined { + if (tsClassification > TokenEncodingConsts.modifierMask) { + return (tsClassification >> TokenEncodingConsts.typeOffset) - 1; + } + return undefined; +} + +function getTokenModifierFromClassification(tsClassification: number) { + return tsClassification & TokenEncodingConsts.modifierMask; +} + +const tokenTypes: string[] = []; +tokenTypes[TokenType.class] = 'class'; +tokenTypes[TokenType.enum] = 'enum'; +tokenTypes[TokenType.interface] = 'interface'; +tokenTypes[TokenType.namespace] = 'namespace'; +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'; + +const tokenModifiers: string[] = []; +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[] = []; +tokenTypeMap[ExperimentalProtocol.ClassificationType.className] = TokenType.class; +tokenTypeMap[ExperimentalProtocol.ClassificationType.enumName] = TokenType.enum; +tokenTypeMap[ExperimentalProtocol.ClassificationType.interfaceName] = TokenType.interface; +tokenTypeMap[ExperimentalProtocol.ClassificationType.moduleName] = TokenType.namespace; +tokenTypeMap[ExperimentalProtocol.ClassificationType.typeParameterName] = TokenType.typeParameter; +tokenTypeMap[ExperimentalProtocol.ClassificationType.typeAliasName] = TokenType.type; +tokenTypeMap[ExperimentalProtocol.ClassificationType.parameterName] = TokenType.parameter; + +namespace ExperimentalProtocol { + + export interface IExtendedTypeScriptServiceClient { + execute( + command: K, + args: ExperimentalProtocol.ExtendedTsServerRequests[K][0], + token: vscode.CancellationToken, + config?: ExecConfig + ): Promise>; + } + + /** + * A request to get encoded semantic classifications for a span in the file + */ + export interface EncodedSemanticClassificationsRequest extends Proto.FileRequest { + arguments: EncodedSemanticClassificationsRequestArgs; + } + + /** + * Arguments for EncodedSemanticClassificationsRequest request. + */ + export interface EncodedSemanticClassificationsRequestArgs extends Proto.FileRequestArgs { + /** + * Start position of the span. + */ + start: number; + /** + * Length of the span. + */ + length: number; + } + + export const enum EndOfLineState { + None, + InMultiLineCommentTrivia, + InSingleQuoteStringLiteral, + InDoubleQuoteStringLiteral, + InTemplateHeadOrNoSubstitutionTemplate, + InTemplateMiddleOrTail, + InTemplateSubstitutionPosition, + } + + export const enum ClassificationType { + comment = 1, + identifier = 2, + keyword = 3, + numericLiteral = 4, + operator = 5, + stringLiteral = 6, + regularExpressionLiteral = 7, + whiteSpace = 8, + text = 9, + punctuation = 10, + className = 11, + enumName = 12, + interfaceName = 13, + moduleName = 14, + typeParameterName = 15, + typeAliasName = 16, + parameterName = 17, + docCommentTagName = 18, + jsxOpenTagName = 19, + jsxCloseTagName = 20, + jsxSelfClosingTagName = 21, + jsxAttribute = 22, + jsxText = 23, + jsxAttributeStringLiteralValue = 24, + bigintLiteral = 25, + } + + export interface EncodedSemanticClassificationsResponse extends Proto.Response { + body?: { + endOfLineState: EndOfLineState; + spans: number[]; + }; + } + + export interface ExtendedTsServerRequests { + 'encodedSemanticClassifications-full': [ExperimentalProtocol.EncodedSemanticClassificationsRequestArgs, ExperimentalProtocol.EncodedSemanticClassificationsResponse]; + } +} diff --git a/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts new file mode 100644 index 0000000000000..93901b9bb25d1 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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'; + +class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { + + public static readonly triggerCharacters = ['(', ',', '<']; + public static readonly retriggerCharacters = [')']; + + public constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + public async provideSignatureHelp( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + context: vscode.SignatureHelpContext, + ): Promise { + const filepath = this.client.toOpenedFilePath(document); + if (!filepath) { + return undefined; + } + + const args: Proto.SignatureHelpRequestArgs = { + ...typeConverters.Position.toFileLocationRequestArgs(filepath, position), + triggerReason: toTsTriggerReason(context) + }; + const response = await this.client.interruptGetErr(() => this.client.execute('signatureHelp', args, token)); + if (response.type !== 'response' || !response.body) { + return undefined; + } + + const info = response.body; + const result = new vscode.SignatureHelp(); + 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 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); + } + return info.argumentIndex; + } + + private convertSignature(item: Proto.SignatureHelpItem) { + const signature = new vscode.SignatureInformation( + Previewer.plain(item.prefixDisplayParts), + Previewer.markdownDocumentation(item.documentation, item.tags.filter(x => x.name !== 'param'))); + + let textIndex = signature.label.length; + const separatorLabel = Previewer.plain(item.separatorDisplayParts); + for (let i = 0; i < item.parameters.length; ++i) { + const parameter = item.parameters[i]; + const label = Previewer.plain(parameter.displayParts); + + signature.parameters.push( + new vscode.ParameterInformation( + [textIndex, textIndex + label.length], + Previewer.markdownDocumentation(parameter.documentation, []))); + + textIndex += label.length; + signature.label += label; + + if (i !== item.parameters.length - 1) { + signature.label += separatorLabel; + textIndex += separatorLabel.length; + } + } + + signature.label += Previewer.plain(item.suffixDisplayParts); + return signature; + } +} + +function toTsTriggerReason(context: vscode.SignatureHelpContext): Proto.SignatureHelpTriggerReason { + switch (context.triggerKind) { + case vscode.SignatureHelpTriggerKind.TriggerCharacter: + if (context.triggerCharacter) { + if (context.isRetrigger) { + return { kind: 'retrigger', triggerCharacter: context.triggerCharacter as any }; + } else { + return { kind: 'characterTyped', triggerCharacter: context.triggerCharacter as any }; + } + } else { + return { kind: 'invoked' }; + } + + case vscode.SignatureHelpTriggerKind.ContentChange: + return context.isRetrigger ? { kind: 'retrigger' } : { kind: 'invoked' }; + + case vscode.SignatureHelpTriggerKind.Invoke: + default: + return { kind: 'invoked' }; + } +} +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient, +) { + 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 3d05d1309a238..f769347f15cfc 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/languageFeatures/tagClosing.ts b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts new file mode 100644 index 0000000000000..289ce73b29370 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts @@ -0,0 +1,164 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +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 { + public static readonly minVersion = API.v300; + + private _disposed = false; + private _timeout: NodeJS.Timer | undefined = undefined; + private _cancel: vscode.CancellationTokenSource | undefined = undefined; + + constructor( + private readonly client: ITypeScriptServiceClient + ) { + super(); + vscode.workspace.onDidChangeTextDocument( + event => this.onDidChangeTextDocument(event.document, event.contentChanges), + null, + this._disposables); + } + + public dispose() { + super.dispose(); + this._disposed = true; + + if (this._timeout) { + clearTimeout(this._timeout); + this._timeout = undefined; + } + + if (this._cancel) { + this._cancel.cancel(); + this._cancel.dispose(); + this._cancel = undefined; + } + } + + private onDidChangeTextDocument( + document: vscode.TextDocument, + changes: readonly vscode.TextDocumentContentChangeEvent[] + ) { + const activeDocument = vscode.window.activeTextEditor && vscode.window.activeTextEditor.document; + if (document !== activeDocument || changes.length === 0) { + return; + } + + const filepath = this.client.toOpenedFilePath(document); + if (!filepath) { + return; + } + + if (typeof this._timeout !== 'undefined') { + clearTimeout(this._timeout); + } + + if (this._cancel) { + this._cancel.cancel(); + this._cancel.dispose(); + this._cancel = undefined; + } + + const lastChange = changes[changes.length - 1]; + const lastCharacter = lastChange.text[lastChange.text.length - 1]; + if (lastChange.rangeLength > 0 || lastCharacter !== '>' && lastCharacter !== '/') { + return; + } + + const priorCharacter = lastChange.range.start.character > 0 + ? document.getText(new vscode.Range(lastChange.range.start.translate({ characterDelta: -1 }), lastChange.range.start)) + : ''; + if (priorCharacter === '>') { + return; + } + + const version = document.version; + this._timeout = setTimeout(async () => { + this._timeout = undefined; + + if (this._disposed) { + return; + } + + const addedLines = lastChange.text.split(/\r\n|\n/g); + const position = addedLines.length <= 1 + ? lastChange.range.start.translate({ characterDelta: lastChange.text.length }) + : new vscode.Position(lastChange.range.start.line + addedLines.length - 1, addedLines[addedLines.length - 1].length); + + const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + this._cancel = new vscode.CancellationTokenSource(); + const response = await this.client.execute('jsxClosingTag', args, this._cancel.token); + if (response.type !== 'response' || !response.body) { + return; + } + + if (this._disposed) { + return; + } + + const activeEditor = vscode.window.activeTextEditor; + if (!activeEditor) { + return; + } + + const insertion = response.body; + const activeDocument = activeEditor.document; + if (document === activeDocument && activeDocument.version === version) { + activeEditor.insertSnippet( + this.getTagSnippet(insertion), + this.getInsertionPositions(activeEditor, position)); + } + }, 100); + } + + private getTagSnippet(closingTag: Proto.TextInsertion): vscode.SnippetString { + const snippet = new vscode.SnippetString(); + snippet.appendPlaceholder('', 0); + snippet.appendText(closingTag.newText); + return snippet; + } + + private getInsertionPositions(editor: vscode.TextEditor, position: vscode.Position) { + const activeSelectionPositions = editor.selections.map(s => s.active); + return activeSelectionPositions.some(p => p.isEqual(position)) + ? activeSelectionPositions + : position; + } +} + +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: DocumentSelector, + modeId: string, + client: ITypeScriptServiceClient, +) { + 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 95% rename from extensions/typescript-language-features/src/features/tsconfig.ts rename to extensions/typescript-language-features/src/languageFeatures/tsconfig.ts index 2a47c0887bd47..e8baac3b6ad24 100644 --- a/extensions/typescript-language-features/src/features/tsconfig.ts +++ b/extensions/typescript-language-features/src/languageFeatures/tsconfig.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as jsonc from 'jsonc-parser'; -import { dirname, join, basename } from 'path'; +import { basename, dirname, join } from 'path'; import * as vscode from 'vscode'; -import { flatten } from '../utils/arrays'; +import { coalesce, flatten } from '../utils/arrays'; function mapChildren(node: jsonc.Node | undefined, f: (x: jsonc.Node) => R): R[] { return node && node.type === 'array' && node.children @@ -25,11 +25,11 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider { return null; } - return [ + return coalesce([ this.getExtendsLink(document, root), ...this.getFilesLinks(document, root), ...this.getReferencesLinks(document, root) - ].filter(x => !!x) as vscode.DocumentLink[]; + ]); } private getExtendsLink(document: vscode.TextDocument, root: jsonc.Node): vscode.DocumentLink | undefined { @@ -68,7 +68,7 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider { } return new vscode.DocumentLink(this.getRange(document, pathNode), - basename(pathNode.value).match('.json$') + basename(pathNode.value).endsWith('.json') ? this.getFileTarget(document, pathNode) : this.getFolderTarget(document, pathNode)); }); diff --git a/extensions/typescript-language-features/src/languageFeatures/typeDefinitions.ts b/extensions/typescript-language-features/src/languageFeatures/typeDefinitions.ts new file mode 100644 index 0000000000000..4aef4f41ed42f --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/typeDefinitions.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 vscode from 'vscode'; +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 { + public provideTypeDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { + return this.getSymbolLocations('typeDefinition', document, position, token); + } +} + +export function register( + selector: DocumentSelector, + client: ITypeScriptServiceClient, +) { + 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 8dad613683cb1..2c6354c01a886 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'; @@ -58,7 +58,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { super(); this._register(vscode.workspace.onDidRenameFiles(async (e) => { - const [{ newUri, oldUri }] = e.renamed; + const [{ newUri, oldUri }] = e.files; const newFilePath = this.client.toPath(newUri); if (!newFilePath) { return; @@ -294,6 +294,10 @@ export function register( fileConfigurationManager: FileConfigurationManager, handles: (uri: vscode.Uri) => Promise, ) { - 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/languageFeatures/workspaceSymbols.ts b/extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts new file mode 100644 index 0000000000000..73a142440e524 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/workspaceSymbols.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * 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 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: readonly string[], + ) { } + + public async provideWorkspaceSymbols( + search: string, + token: vscode.CancellationToken + ): Promise { + let file: string | undefined; + if (this.searchAllOpenProjects) { + file = undefined; + } else { + const document = this.getDocument(); + file = document ? await this.toOpenedFiledPath(document) : undefined; + + if (!file && this.client.apiVersion.lt(API.v390)) { + return []; + } + } + + const args: Proto.NavtoRequestArgs = { + file, + searchValue: search, + maxResultCount: 256, + }; + + const response = await this.client.execute('navto', args, token); + if (response.type !== 'response' || !response.body) { + return []; + } + + 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) { + if (document.uri.scheme === fileSchemes.git) { + try { + const path = vscode.Uri.file(JSON.parse(document.uri.query)?.path); + if (doesResourceLookLikeATypeScriptFile(path) || doesResourceLookLikeAJavaScriptFile(path)) { + const document = await vscode.workspace.openTextDocument(path); + return this.client.toOpenedFilePath(document); + } + } catch { + // noop + } + } + 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') { + return label + '()'; + } + return label; + } + + private getDocument(): vscode.TextDocument | undefined { + // typescript wants to have a resource even when asking + // general questions so we check the active editor. If this + // doesn't match we take the first TS document. + + const activeDocument = vscode.window.activeTextEditor?.document; + if (activeDocument) { + if (this.modeIds.includes(activeDocument.languageId)) { + return activeDocument; + } + } + + const documents = vscode.workspace.textDocuments; + for (const document of documents) { + if (this.modeIds.includes(document.languageId)) { + return document; + } + } + return undefined; + } +} + +export function register( + client: ITypeScriptServiceClient, + modeIds: readonly string[], +) { + 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 4210ba2ba20fd..92ab84be30892 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -5,16 +5,16 @@ 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 { 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 { @@ -56,28 +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('./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))), ]); } @@ -120,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('showUnused', true); + const reportDeprecated = config.get('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; })); } @@ -141,4 +151,4 @@ export default class LanguageProvider extends Disposable { private get _diagnosticLanguage() { return this.description.diagnosticLanguage; } -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/lazyClientHost.ts b/extensions/typescript-language-features/src/lazyClientHost.ts new file mode 100644 index 0000000000000..73dee25e6c377 --- /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 { + return lazy(() => { + const clientHost = new TypeScriptServiceClientHost( + standardLanguageDescriptions, + context.workspaceState, + onCaseInsenitiveFileSystem, + services, + onCompletionAccepted); + + context.subscriptions.push(clientHost); + + return clientHost; + }); +} + +export function lazilyActivateClient( + lazyClientHost: Lazy, + 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 efded538f7708..210e962c9aa52 100644 --- a/extensions/typescript-language-features/src/protocol.const.ts +++ b/extensions/typescript-language-features/src/protocol.const.ts @@ -12,6 +12,7 @@ export class Kind { public static readonly constructSignature = 'construct'; public static readonly directory = 'directory'; public static readonly enum = 'enum'; + public static readonly enumMember = 'enum member'; public static readonly externalModuleName = 'external module name'; public static readonly function = 'function'; public static readonly indexSignature = 'index'; @@ -20,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'; @@ -32,6 +33,7 @@ export class Kind { public static readonly warning = 'warning'; public static readonly string = 'string'; public static readonly parameter = 'parameter'; + public static readonly typeParameter = 'type parameter'; } @@ -43,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'; @@ -69,4 +72,20 @@ export class DisplayPartKind { public static readonly propertyName = 'propertyName'; public static readonly punctuation = 'punctuation'; public static readonly text = 'text'; -} \ No newline at end of file +} + +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 6e926eb8d7ee2..e81fe81f2dbb4 100644 --- a/extensions/typescript-language-features/src/protocol.d.ts +++ b/extensions/typescript-language-features/src/protocol.d.ts @@ -1,2 +1,12 @@ import * as Proto from 'typescript/lib/protocol'; export = Proto; + +declare enum ServerType { + Syntax = 'syntax', + Semantic = 'semantic', +} +declare module 'typescript/lib/protocol' { + interface Response { + readonly _serverType?: ServerType; + } +} diff --git a/extensions/typescript-language-features/src/task/taskProvider.ts b/extensions/typescript-language-features/src/task/taskProvider.ts new file mode 100644 index 0000000000000..85d3b574ae32a --- /dev/null +++ b/extensions/typescript-language-features/src/task/taskProvider.ts @@ -0,0 +1,299 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as jsonc from 'jsonc-parser'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { isTsConfigFileName } from '../utils/languageDescription'; +import { Lazy } from '../utils/lazy'; +import { isImplicitProjectConfigFile } from '../utils/tsconfig'; +import { TSConfig, TsConfigProvider } from './tsconfigProvider'; + +const localize = nls.loadMessageBundle(); + +type AutoDetect = 'on' | 'off' | 'build' | 'watch'; + +const exists = async (resource: vscode.Uri): Promise => { + 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; + option?: string; +} + +/** + * Provides tasks for building `tsconfig.json` files in a project. + */ +class TscTaskProvider implements vscode.TaskProvider { + + private readonly projectInfoRequestTimeout = 2000; + private autoDetect: AutoDetect = 'on'; + private readonly tsconfigProvider: TsConfigProvider; + private readonly disposables: vscode.Disposable[] = []; + + public constructor( + private readonly client: Lazy + ) { + this.tsconfigProvider = new TsConfigProvider(); + + vscode.workspace.onDidChangeConfiguration(this.onConfigurationChanged, this, this.disposables); + this.onConfigurationChanged(); + } + + dispose() { + this.disposables.forEach(x => x.dispose()); + } + + public async provideTasks(token: vscode.CancellationToken): Promise { + const folders = vscode.workspace.workspaceFolders; + if ((this.autoDetect === 'off') || !folders || !folders.length) { + return []; + } + + const configPaths: Set = new Set(); + const tasks: vscode.Task[] = []; + for (const project of await this.getAllTsConfigs(token)) { + if (!configPaths.has(project.fsPath)) { + configPaths.add(project.fsPath); + tasks.push(...(await this.getTasksForProject(project))); + } + } + return tasks; + } + + public async resolveTask(task: vscode.Task): Promise { + const definition = task.definition; + if (/\\tsconfig.*\.json/.test(definition.tsconfig)) { + // Warn that the task has the wrong slash type + vscode.window.showWarningMessage(localize('badTsConfig', "TypeScript Task in tasks.json contains \"\\\\\". TypeScript tasks tsconfig must use \"/\"")); + return undefined; + } + + const tsconfigPath = definition.tsconfig; + if (!tsconfigPath) { + return undefined; + } + + if (task.scope === undefined || task.scope === vscode.TaskScope.Global || task.scope === vscode.TaskScope.Workspace) { + // scope is required to be a WorkspaceFolder for resolveTask + return undefined; + } + const tsconfigUri = task.scope.uri.with({ path: task.scope.uri.path + '/' + tsconfigPath }); + const tsconfig: TSConfig = { + uri: tsconfigUri, + fsPath: tsconfigUri.fsPath, + posixPath: tsconfigUri.path, + workspaceFolder: task.scope + }; + return this.getTasksForProjectAndDefinition(tsconfig, definition); + } + + private async getAllTsConfigs(token: vscode.CancellationToken): Promise { + const out = new Set(); + 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); + } + + private async getTsConfigForActiveFile(token: vscode.CancellationToken): Promise { + const editor = vscode.window.activeTextEditor; + if (editor) { + if (isTsConfigFileName(editor.document.fileName)) { + const uri = editor.document.uri; + return [{ + uri, + fsPath: uri.fsPath, + posixPath: uri.path, + workspaceFolder: vscode.workspace.getWorkspaceFolder(uri) + }]; + } + } + + const file = this.getActiveTypeScriptFile(); + if (!file) { + return []; + } + + const response = await Promise.race([ + this.client.value.execute( + 'projectInfo', + { file, needFileNameList: false }, + token), + new Promise(resolve => setTimeout(() => resolve(ServerResponse.NoContent), this.projectInfoRequestTimeout)) + ]); + if (response.type !== 'response' || !response.body) { + return []; + } + + const { configFileName } = response.body; + if (configFileName && !isImplicitProjectConfigFile(configFileName)) { + const normalizedConfigPath = path.normalize(configFileName); + const uri = vscode.Uri.file(normalizedConfigPath); + const folder = vscode.workspace.getWorkspaceFolder(uri); + return [{ + uri, + fsPath: normalizedConfigPath, + posixPath: uri.path, + workspaceFolder: folder + }]; + } + + return []; + } + + private async getTsConfigsInWorkspace(): Promise { + return Array.from(await this.tsconfigProvider.getConfigsForWorkspace()); + } + + private static async getCommand(project: TSConfig): Promise { + if (project.workspaceFolder) { + const localTsc = await TscTaskProvider.getLocalTscAtPath(path.dirname(project.fsPath)); + if (localTsc) { + return localTsc; + } + + const workspaceTsc = await TscTaskProvider.getLocalTscAtPath(project.workspaceFolder.uri.fsPath); + if (workspaceTsc) { + return workspaceTsc; + } + } + + // Use global tsc version + return 'tsc'; + } + + private static async getLocalTscAtPath(folderPath: string): Promise { + const platform = process.platform; + const bin = path.join(folderPath, 'node_modules', '.bin'); + if (platform === 'win32' && await exists(vscode.Uri.file(path.join(bin, 'tsc.cmd')))) { + return path.join(bin, 'tsc.cmd'); + } else if ((platform === 'linux' || platform === 'darwin') && await exists(vscode.Uri.file(path.join(bin, 'tsc')))) { + return path.join(bin, 'tsc'); + } + return undefined; + } + + private getActiveTypeScriptFile(): string | undefined { + const editor = vscode.window.activeTextEditor; + if (editor) { + const document = editor.document; + if (document && (document.languageId === 'typescript' || document.languageId === 'typescriptreact')) { + return this.client.value.toPath(document.uri); + } + } + return undefined; + } + + private getBuildTask(workspaceFolder: vscode.WorkspaceFolder | undefined, label: string, command: string, args: string[], buildTaskidentifier: TypeScriptTaskDefinition): vscode.Task { + const buildTask = new vscode.Task( + buildTaskidentifier, + workspaceFolder || vscode.TaskScope.Workspace, + localize('buildTscLabel', 'build - {0}', label), + 'tsc', + new vscode.ShellExecution(command, args), + '$tsc'); + buildTask.group = vscode.TaskGroup.Build; + buildTask.isBackground = false; + return buildTask; + } + + private getWatchTask(workspaceFolder: vscode.WorkspaceFolder | undefined, label: string, command: string, args: string[], watchTaskidentifier: TypeScriptTaskDefinition) { + const watchTask = new vscode.Task( + watchTaskidentifier, + workspaceFolder || vscode.TaskScope.Workspace, + localize('buildAndWatchTscLabel', 'watch - {0}', label), + 'tsc', + new vscode.ShellExecution(command, [...args, '--watch']), + '$tsc-watch'); + watchTask.group = vscode.TaskGroup.Build; + watchTask.isBackground = true; + return watchTask; + } + + private async getTasksForProject(project: TSConfig): Promise { + const command = await TscTaskProvider.getCommand(project); + const args = await this.getBuildShellArgs(project); + const label = this.getLabelForTasks(project); + + const tasks: vscode.Task[] = []; + + if (this.autoDetect === 'build' || this.autoDetect === 'on') { + tasks.push(this.getBuildTask(project.workspaceFolder, label, command, args, { type: 'typescript', tsconfig: label })); + } + + if (this.autoDetect === 'watch' || this.autoDetect === 'on') { + tasks.push(this.getWatchTask(project.workspaceFolder, label, command, args, { type: 'typescript', tsconfig: label, option: 'watch' })); + } + + return tasks; + } + + private async getTasksForProjectAndDefinition(project: TSConfig, definition: TypeScriptTaskDefinition): Promise { + const command = await TscTaskProvider.getCommand(project); + const args = await this.getBuildShellArgs(project); + const label = this.getLabelForTasks(project); + + let task: vscode.Task | undefined; + + if (definition.option === undefined) { + task = this.getBuildTask(project.workspaceFolder, label, command, args, definition); + } else if (definition.option === 'watch') { + task = this.getWatchTask(project.workspaceFolder, label, command, args, definition); + } + + return task; + } + + private async getBuildShellArgs(project: TSConfig): Promise> { + const defaultArgs = ['-p', project.fsPath]; + try { + const bytes = await vscode.workspace.fs.readFile(project.uri); + const text = Buffer.from(bytes).toString('utf-8'); + const tsconfig = jsonc.parse(text); + if (tsconfig?.references) { + return ['-b', project.fsPath]; + } + } catch { + // noops + } + return defaultArgs; + } + + private getLabelForTasks(project: TSConfig): string { + if (project.workspaceFolder) { + const workspaceNormalizedUri = vscode.Uri.file(path.normalize(project.workspaceFolder.uri.fsPath)); // Make sure the drive letter is lowercase + return path.posix.relative(workspaceNormalizedUri.path, project.posixPath); + } + + return project.posixPath; + } + + private onConfigurationChanged(): void { + const type = vscode.workspace.getConfiguration('typescript.tsc').get('autoDetect'); + this.autoDetect = typeof type === 'undefined' ? 'on' : type; + } +} + +export function register( + lazyClient: Lazy, +) { + 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 84% rename from extensions/typescript-language-features/src/utils/tsconfigProvider.ts rename to extensions/typescript-language-features/src/task/tsconfigProvider.ts index fe939032029dd..d44b828e38432 100644 --- a/extensions/typescript-language-features/src/utils/tsconfigProvider.ts +++ b/extensions/typescript-language-features/src/task/tsconfigProvider.ts @@ -2,25 +2,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 vscode from 'vscode'; export interface TSConfig { - readonly path: string; + readonly uri: vscode.Uri; + readonly fsPath: string; readonly posixPath: string; readonly workspaceFolder?: vscode.WorkspaceFolder; } -export default class TsConfigProvider { +export class TsConfigProvider { public async getConfigsForWorkspace(): Promise> { if (!vscode.workspace.workspaceFolders) { return []; } const configs = new Map(); - for (const config of await vscode.workspace.findFiles('**/tsconfig*.json', '**/node_modules/**')) { + for (const config of await vscode.workspace.findFiles('**/tsconfig*.json', '**/{node_modules,.*}/**')) { const root = vscode.workspace.getWorkspaceFolder(config); if (root) { configs.set(config.fsPath, { - path: config.fsPath, + uri: config, + fsPath: config.fsPath, posixPath: config.path, workspaceFolder: root }); diff --git a/extensions/typescript-language-features/src/test/cachedResponse.test.ts b/extensions/typescript-language-features/src/test/cachedResponse.test.ts index 919d5d4d03cce..ae05f5d442654 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 f35d0cae73c9a..92a7da63f92b8 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 { - 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, + 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() {`, + ` ab$0c`, + ` }`, + `}`, + ); + + 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 {`, - ` private detail = '';`, + ` 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).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() {`, - ` det$0`, + ` this.$0abc`, ` }`, `}`, ); - const document = await acceptFirstSuggestion(testDocumentUri, _disposables); - assert.strictEqual( - document.getText(), + await acceptFirstSuggestion(testDocumentUri, _disposables); + + assertEditorContents(editor, joinLines( `class A {`, - ` private detail = '';`, + ` 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 0000000000000..37fae0bcfc8e3 --- /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.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.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.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.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 e053719f3d4e4..5c8cf18c73c02 100644 --- a/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts +++ b/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts @@ -3,6 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// todo@matt +/* eslint code-no-unexternalized-strings: 0 */ + import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; @@ -125,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/index.ts b/extensions/typescript-language-features/src/test/index.ts index f65a756a8dea0..1a5f741905998 100644 --- a/extensions/typescript-language-features/src/test/index.ts +++ b/extensions/typescript-language-features/src/test/index.ts @@ -21,8 +21,8 @@ const testRunner = require('vscode/lib/testrunner'); // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info testRunner.configure({ ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) - useColors: process.platform !== 'win32', // colored output from test results (only windows cannot handle) - timeout: 60000 + useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), // colored output from test results (only windows cannot handle) + timeout: 60000, }); export = testRunner; diff --git a/extensions/typescript-language-features/src/test/jsDocCompletions.test.ts b/extensions/typescript-language-features/src/test/jsDocCompletions.test.ts index ffd836f5ab3e7..63d3f144f9a90 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) { }`, - ); - - const document = await acceptFirstSuggestion(testDocumentUri, _disposables, { useLineRange: true}); - assert.strictEqual( - document.getText(), - joinLines( - `/**`, - ` *`, - ` * @param {*} x `, - ` * @param {*} y `, - ` */`, + await enumerateConfig(testDocumentUri, Config.insertMode, insertModesValues, async config => { + + 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 a982e46221f77..d9e17a756447f 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 c7d6c3a45dd34..72e59b4482fa3 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 => { return new Promise(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 =
${CURSOR}`, 'jsx', async (_editor, document) => { - await type(document, '\nx'); - assert.strictEqual(document.getText(), `const a =
\n x`); - }); - }); - - test('should indent after simple jsx tag with attributes', () => { - return withRandomFileEditor(`const a =
${CURSOR}`, 'jsx', async (_editor, document) => { - await type(document, '\nx'); - assert.strictEqual(document.getText(), `const a =
\n x`); + assert.strictEqual( + document.getText(), + joinLines(`({`, + ` 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 011187b2af4de..b92c001963102 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 0000000000000..13200dbcf5096 --- /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.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.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.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.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 new file mode 100644 index 0000000000000..f40edb2ca5d7c --- /dev/null +++ b/extensions/typescript-language-features/src/test/referencesCodeLens.test.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 * as assert from 'assert'; +import 'mocha'; +import * as vscode from 'vscode'; +import { disposeAll } from '../utils/dispose'; +import { createTestEditor, wait } from './testUtils'; + + +type VsCodeConfiguration = { [key: string]: any }; + +async function updateConfig(newConfig: VsCodeConfiguration): Promise { + const oldConfig: VsCodeConfiguration = {}; + const config = vscode.workspace.getConfiguration(undefined); + 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 referencesCodeLens = 'typescript.referencesCodeLens.enabled'; +} + +suite('TypeScript References', () => { + const configDefaults: VsCodeConfiguration = Object.freeze({ + [Config.referencesCodeLens]: true, + }); + + const _disposables: vscode.Disposable[] = []; + let oldConfig: { [key: string]: any } = {}; + + setup(async () => { + await wait(100); + + // Save off config and apply defaults + oldConfig = await updateConfig(configDefaults); + }); + + teardown(async () => { + disposeAll(_disposables); + + // Restore config + await updateConfig(oldConfig); + + return vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Should show on basic class', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test1.ts'); + await createTestEditor(testDocumentUri, + `class Foo {}` + ); + + const codeLenses = await getCodeLenses(testDocumentUri); + assert.strictEqual(codeLenses?.length, 1); + assert.strictEqual(codeLenses?.[0].range.start.line, 0); + }); + + test('Should show on basic class properties', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test2.ts'); + await createTestEditor(testDocumentUri, + `class Foo {`, + ` prop: number;`, + ` meth(): void {}`, + `}` + ); + + const codeLenses = await getCodeLenses(testDocumentUri); + assert.strictEqual(codeLenses?.length, 3); + assert.strictEqual(codeLenses?.[0].range.start.line, 0); + assert.strictEqual(codeLenses?.[1].range.start.line, 1); + assert.strictEqual(codeLenses?.[2].range.start.line, 2); + }); + + test('Should not show on const property', async () => { + const testDocumentUri = vscode.Uri.parse('untitled:test3.ts'); + await createTestEditor(testDocumentUri, + `const foo = {`, + ` prop: 1;`, + ` meth(): void {}`, + `}` + ); + + 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 { + return vscode.commands.executeCommand('vscode.executeCodeLensProvider', document, 100); +} + diff --git a/extensions/typescript-language-features/src/test/server.test.ts b/extensions/typescript-language-features/src/test/server.test.ts index 651967fc12c02..5caad737a48db 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 TelemetryReporter from '../utils/telemetry'; +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 7c64a26ad5c1b..f417ceaadb57b 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(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 { - // 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 753814a99bd4b..ea5e41a26a814 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(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'); + 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 { + 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((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 +): Promise { + for (const value of values) { + const newConfig = { [configKey]: value }; + await updateConfig(documentUri, newConfig); + await f(JSON.stringify(newConfig)); } +} + - await activeEditor.insertSnippet(new vscode.SnippetString(joinLines(...lines)), new vscode.Range(0, 0, 1000, 0)); +export function onChangedDocument(documentUri: vscode.Uri, disposables: vscode.Disposable[]) { + return new Promise(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, +) { + 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/tsServer/bufferSyncSupport.ts b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts new file mode 100644 index 0000000000000..40610b66f115f --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts @@ -0,0 +1,619 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ITypeScriptServiceClient, ClientCapability } from '../typescriptService'; +import API from '../utils/api'; +import { coalesce } from '../utils/arrays'; +import { Delayer } from '../utils/async'; +import { nulToken } from '../utils/cancellation'; +import { Disposable } from '../utils/dispose'; +import * as languageModeIds from '../utils/languageModeIds'; +import { ResourceMap } from '../utils/resourceMap'; +import * as typeConverters from '../utils/typeConverters'; + +const enum BufferKind { + TypeScript = 1, + JavaScript = 2, +} + +const enum BufferState { + Initial = 1, + Open = 2, + Closed = 2, +} + +function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined { + switch (mode) { + case languageModeIds.typescript: return 'TS'; + case languageModeIds.typescriptreact: return 'TSX'; + case languageModeIds.javascript: return 'JS'; + case languageModeIds.javascriptreact: return 'JSX'; + } + return undefined; +} + +const enum BufferOperationType { Close, Open, Change } + +class CloseOperation { + readonly type = BufferOperationType.Close; + constructor( + public readonly args: string + ) { } +} + +class OpenOperation { + readonly type = BufferOperationType.Open; + constructor( + public readonly args: Proto.OpenRequestArgs + ) { } +} + +class ChangeOperation { + readonly type = BufferOperationType.Change; + constructor( + public readonly args: Proto.FileCodeEdits + ) { } +} + +type BufferOperation = CloseOperation | OpenOperation | ChangeOperation; + +/** + * Manages synchronization of buffers with the TS server. + * + * If supported, batches together file changes. This allows the TS server to more efficiently process changes. + */ +class BufferSynchronizer { + + private readonly _pending: ResourceMap; + + constructor( + private readonly client: ITypeScriptServiceClient, + onCaseInsenitiveFileSystem: boolean + ) { + this._pending = new ResourceMap(undefined, { + onCaseInsenitiveFileSystem + }); + } + + public open(resource: vscode.Uri, args: Proto.OpenRequestArgs) { + if (this.supportsBatching) { + this.updatePending(resource, new OpenOperation(args)); + } else { + this.client.executeWithoutWaitingForResponse('open', args); + } + } + + /** + * @return Was the buffer open? + */ + public close(resource: vscode.Uri, filepath: string): boolean { + if (this.supportsBatching) { + return this.updatePending(resource, new CloseOperation(filepath)); + } else { + const args: Proto.FileRequestArgs = { file: filepath }; + this.client.executeWithoutWaitingForResponse('close', args); + return true; + } + } + + public change(resource: vscode.Uri, filepath: string, events: readonly vscode.TextDocumentContentChangeEvent[]) { + if (!events.length) { + return; + } + + if (this.supportsBatching) { + this.updatePending(resource, new ChangeOperation({ + fileName: filepath, + textChanges: events.map((change): Proto.CodeEdit => ({ + newText: change.text, + start: typeConverters.Position.toLocation(change.range.start), + end: typeConverters.Position.toLocation(change.range.end), + })).reverse(), // Send the edits end-of-document to start-of-document order + })); + } else { + for (const { range, text } of events) { + const args: Proto.ChangeRequestArgs = { + insertString: text, + ...typeConverters.Range.toFormattingRequestArgs(filepath, range) + }; + this.client.executeWithoutWaitingForResponse('change', args); + } + } + } + + public reset(): void { + this._pending.clear(); + } + + public beforeCommand(command: string): void { + if (command === 'updateOpen') { + return; + } + + this.flush(); + } + + private flush() { + if (!this.supportsBatching) { + // We've already eagerly synchronized + this._pending.clear(); + return; + } + + if (this._pending.size > 0) { + const closedFiles: string[] = []; + const openFiles: Proto.OpenRequestArgs[] = []; + const changedFiles: Proto.FileCodeEdits[] = []; + for (const change of this._pending.values) { + switch (change.type) { + case BufferOperationType.Change: changedFiles.push(change.args); break; + case BufferOperationType.Open: openFiles.push(change.args); break; + case BufferOperationType.Close: closedFiles.push(change.args); break; + } + } + this.client.execute('updateOpen', { changedFiles, closedFiles, openFiles }, nulToken, { nonRecoverable: true }); + this._pending.clear(); + } + } + + private get supportsBatching(): boolean { + return this.client.apiVersion.gte(API.v340); + } + + 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 false; // Open then close. No need to do anything + } + break; + } + + if (this._pending.has(resource)) { + // we saw this file before, make sure we flush before working with it again + this.flush(); + } + this._pending.set(resource, op); + return true; + } +} + +class SyncedBuffer { + + private state = BufferState.Initial; + + constructor( + private readonly document: vscode.TextDocument, + public readonly filepath: string, + private readonly client: ITypeScriptServiceClient, + private readonly synchronizer: BufferSynchronizer, + ) { } + + public open(): void { + const args: Proto.OpenRequestArgs = { + file: this.filepath, + fileContent: this.document.getText(), + projectRootPath: this.client.getWorkspaceRootForResource(this.document.uri), + }; + + const scriptKind = mode2ScriptKind(this.document.languageId); + if (scriptKind) { + args.scriptKindName = scriptKind; + } + + if (this.client.apiVersion.gte(API.v240)) { + const tsPluginsForDocument = this.client.pluginManager.plugins + .filter(x => x.languages.indexOf(this.document.languageId) >= 0); + + if (tsPluginsForDocument.length) { + (args as any).plugins = tsPluginsForDocument.map(plugin => plugin.name); + } + } + + this.synchronizer.open(this.resource, args); + this.state = BufferState.Open; + } + + public get resource(): vscode.Uri { + return this.document.uri; + } + + public get lineCount(): number { + return this.document.lineCount; + } + + public get kind(): BufferKind { + switch (this.document.languageId) { + case languageModeIds.javascript: + case languageModeIds.javascriptreact: + return BufferKind.JavaScript; + + case languageModeIds.typescript: + case languageModeIds.typescriptreact: + default: + return BufferKind.TypeScript; + } + } + + /** + * @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 { + if (this.state !== BufferState.Open) { + console.error(`Unexpected buffer state: ${this.state}`); + } + + this.synchronizer.change(this.resource, this.filepath, events); + } +} + +class SyncedBufferMap extends ResourceMap { + + public getForPath(filePath: string): SyncedBuffer | undefined { + return this.get(vscode.Uri.file(filePath)); + } + + public get allBuffers(): Iterable { + return this.values; + } +} + +class PendingDiagnostics extends ResourceMap { + public getOrderedFileSet(): ResourceMap { + const orderedResources = Array.from(this.entries) + .sort((a, b) => a.value - b.value) + .map(entry => entry.resource); + + const map = new ResourceMap(undefined, this.config); + for (const resource of orderedResources) { + map.set(resource, undefined); + } + return map; + } +} + +class GetErrRequest { + + public static executeGetErrRequest( + client: ITypeScriptServiceClient, + files: ResourceMap, + onDone: () => void + ) { + return new GetErrRequest(client, files, onDone); + } + + private _done: boolean = false; + private readonly _token: vscode.CancellationTokenSource = new vscode.CancellationTokenSource(); + + private constructor( + client: ITypeScriptServiceClient, + public readonly files: ResourceMap, + onDone: () => void + ) { + const allFiles = coalesce(Array.from(files.entries) + .filter(entry => client.hasCapabilityForResource(entry.resource, ClientCapability.Semantic)) + .map(entry => client.normalizedPath(entry.resource))); + + 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 { + if (!this._done) { + this._token.cancel(); + } + + this._token.dispose(); + } +} + +export default class BufferSyncSupport extends Disposable { + + private readonly client: ITypeScriptServiceClient; + + private _validateJavaScript: boolean = true; + private _validateTypeScript: boolean = true; + private readonly modeIds: Set; + private readonly syncedBuffers: SyncedBufferMap; + private readonly pendingDiagnostics: PendingDiagnostics; + private readonly diagnosticDelayer: Delayer; + private pendingGetErr: GetErrRequest | undefined; + private listening: boolean = false; + private readonly synchronizer: BufferSynchronizer; + + constructor( + client: ITypeScriptServiceClient, + modeIds: readonly string[], + onCaseInsenitiveFileSystem: boolean + ) { + super(); + this.client = client; + this.modeIds = new Set(modeIds); + + this.diagnosticDelayer = new Delayer(300); + + const pathNormalizer = (path: vscode.Uri) => this.client.normalizedPath(path); + 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); + } + + private readonly _onDelete = this._register(new vscode.EventEmitter()); + public readonly onDelete = this._onDelete.event; + + private readonly _onWillChange = this._register(new vscode.EventEmitter()); + public readonly onWillChange = this._onWillChange.event; + + public listen(): void { + if (this.listening) { + return; + } + this.listening = true; + vscode.workspace.onDidOpenTextDocument(this.openTextDocument, this, this._disposables); + vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this._disposables); + vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this._disposables); + vscode.window.onDidChangeVisibleTextEditors(e => { + for (const { document } of e) { + const syncedBuffer = this.syncedBuffers.get(document.uri); + if (syncedBuffer) { + this.requestDiagnostic(syncedBuffer); + } + } + }, this, this._disposables); + vscode.workspace.textDocuments.forEach(this.openTextDocument, this); + } + + public handles(resource: vscode.Uri): boolean { + return this.syncedBuffers.has(resource); + } + + public ensureHasBuffer(resource: vscode.Uri): boolean { + if (this.syncedBuffers.has(resource)) { + return true; + } + + const existingDocument = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === resource.toString()); + if (existingDocument) { + return this.openTextDocument(existingDocument); + } + + return false; + } + + public toVsCodeResource(resource: vscode.Uri): vscode.Uri { + const filepath = this.client.normalizedPath(resource); + for (const buffer of this.syncedBuffers.allBuffers) { + if (buffer.filepath === filepath) { + return buffer.resource; + } + } + return resource; + } + + public toResource(filePath: string): vscode.Uri { + const buffer = this.syncedBuffers.getForPath(filePath); + if (buffer) { + return buffer.resource; + } + return vscode.Uri.file(filePath); + } + + public reset(): void { + this.pendingGetErr?.cancel(); + this.pendingDiagnostics.clear(); + this.synchronizer.reset(); + } + + public reinitialize(): void { + this.reset(); + for (const buffer of this.syncedBuffers.allBuffers) { + buffer.open(); + } + } + + public openTextDocument(document: vscode.TextDocument): boolean { + if (!this.modeIds.has(document.languageId)) { + return false; + } + const resource = document.uri; + const filepath = this.client.normalizedPath(resource); + if (!filepath) { + return false; + } + + if (this.syncedBuffers.has(resource)) { + return true; + } + + const syncedBuffer = new SyncedBuffer(document, filepath, this.client, this.synchronizer); + this.syncedBuffers.set(resource, syncedBuffer); + syncedBuffer.open(); + this.requestDiagnostic(syncedBuffer); + return true; + } + + public closeResource(resource: vscode.Uri): void { + const syncedBuffer = this.syncedBuffers.get(resource); + if (!syncedBuffer) { + return; + } + this.pendingDiagnostics.delete(resource); + this.pendingGetErr?.files.delete(resource); + this.syncedBuffers.delete(resource); + const wasBufferOpen = syncedBuffer.close(); + this._onDelete.fire(resource); + if (wasBufferOpen) { + this.requestAllDiagnostics(); + } + } + + public interuptGetErr(f: () => R): R { + if (!this.pendingGetErr + || this.client.configuration.enableProjectDiagnostics // `geterr` happens on seperate server so no need to cancel it. + ) { + return f(); + } + + this.pendingGetErr.cancel(); + this.pendingGetErr = undefined; + const result = f(); + this.triggerDiagnostics(); + return result; + } + + public beforeCommand(command: string): void { + this.synchronizer.beforeCommand(command); + } + + private onDidCloseTextDocument(document: vscode.TextDocument): void { + this.closeResource(document.uri); + } + + private onDidChangeTextDocument(e: vscode.TextDocumentChangeEvent): void { + const syncedBuffer = this.syncedBuffers.get(e.document.uri); + if (!syncedBuffer) { + return; + } + + this._onWillChange.fire(syncedBuffer.resource); + + syncedBuffer.onContentChanged(e.contentChanges); + const didTrigger = this.requestDiagnostic(syncedBuffer); + + if (!didTrigger && this.pendingGetErr) { + // In this case we always want to re-trigger all diagnostics + this.pendingGetErr.cancel(); + this.pendingGetErr = undefined; + this.triggerDiagnostics(); + } + } + + public requestAllDiagnostics() { + for (const buffer of this.syncedBuffers.allBuffers) { + if (this.shouldValidate(buffer)) { + this.pendingDiagnostics.set(buffer.resource, Date.now()); + } + } + this.triggerDiagnostics(); + } + + public getErr(resources: readonly vscode.Uri[]): any { + const handledResources = resources.filter(resource => this.handles(resource)); + if (!handledResources.length) { + return; + } + + for (const resource of handledResources) { + this.pendingDiagnostics.set(resource, Date.now()); + } + + this.triggerDiagnostics(); + } + + private triggerDiagnostics(delay: number = 200) { + this.diagnosticDelayer.trigger(() => { + this.sendPendingDiagnostics(); + }, delay); + } + + private requestDiagnostic(buffer: SyncedBuffer): boolean { + if (!this.shouldValidate(buffer)) { + return false; + } + + this.pendingDiagnostics.set(buffer.resource, Date.now()); + + const delay = Math.min(Math.max(Math.ceil(buffer.lineCount / 20), 300), 800); + this.triggerDiagnostics(delay); + return true; + } + + public hasPendingDiagnostics(resource: vscode.Uri): boolean { + return this.pendingDiagnostics.has(resource); + } + + private sendPendingDiagnostics(): void { + const orderedFileSet = this.pendingDiagnostics.getOrderedFileSet(); + + if (this.pendingGetErr) { + this.pendingGetErr.cancel(); + + for (const { resource } of this.pendingGetErr.files.entries) { + if (this.syncedBuffers.get(resource)) { + orderedFileSet.set(resource, undefined); + } + } + + this.pendingGetErr = undefined; + } + + // Add all open TS buffers to the geterr request. They might be visible + for (const buffer of this.syncedBuffers.values) { + orderedFileSet.set(buffer.resource, undefined); + } + + if (orderedFileSet.size) { + const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, orderedFileSet, () => { + if (this.pendingGetErr === getErr) { + this.pendingGetErr = undefined; + } + }); + } + + this.pendingDiagnostics.clear(); + } + + private updateConfiguration() { + const jsConfig = vscode.workspace.getConfiguration('javascript', null); + const tsConfig = vscode.workspace.getConfiguration('typescript', null); + + this._validateJavaScript = jsConfig.get('validate.enable', true); + this._validateTypeScript = tsConfig.get('validate.enable', true); + } + + private shouldValidate(buffer: SyncedBuffer) { + switch (buffer.kind) { + case BufferKind.JavaScript: + return this._validateJavaScript; + + case BufferKind.TypeScript: + default: + return this._validateTypeScript; + } + } +} diff --git a/extensions/typescript-language-features/src/tsServer/cachedResponse.ts b/extensions/typescript-language-features/src/tsServer/cachedResponse.ts index 2fb9b54228160..baa1a87a88ad2 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 = () => Promise>; diff --git a/extensions/typescript-language-features/src/tsServer/callbackMap.ts b/extensions/typescript-language-features/src/tsServer/callbackMap.ts index 7f96de571598c..4e0ec375ddc2f 100644 --- a/extensions/typescript-language-features/src/tsServer/callbackMap.ts +++ b/extensions/typescript-language-features/src/tsServer/callbackMap.ts @@ -3,13 +3,13 @@ * 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 { readonly onSuccess: (value: R) => void; readonly onError: (err: Error) => void; - readonly startTime: number; + readonly queuingStartTime: number; readonly isAsync: boolean; } @@ -48,4 +48,4 @@ export class CallbackMap { this._asyncCallbacks.delete(seq); } } -} \ No newline at end of file +} 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 0000000000000..853ca0c1594b4 --- /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 0000000000000..0eda4e574dc5e --- /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/tsServer/logDirectoryProvider.electron.ts b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.electron.ts new file mode 100644 index 0000000000000..60b6965b5a791 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.electron.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ILogDirectoryProvider } from './logDirectoryProvider'; +import { memoize } from '../utils/memoize'; + +export class NodeLogDirectoryProvider implements ILogDirectoryProvider { + public constructor( + private readonly context: vscode.ExtensionContext + ) { } + + public getNewLogDirectory(): string | undefined { + const root = this.logDirectory(); + if (root) { + try { + return fs.mkdtempSync(path.join(root, `tsserver-log-`)); + } catch (e) { + return undefined; + } + } + return undefined; + } + + @memoize + private logDirectory(): string | undefined { + try { + const path = this.context.logPath; + if (!fs.existsSync(path)) { + fs.mkdirSync(path); + } + return this.context.logPath; + } catch { + return undefined; + } + } +} diff --git a/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.ts b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.ts new file mode 100644 index 0000000000000..75ef2316309fb --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/logDirectoryProvider.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. + *--------------------------------------------------------------------------------------------*/ + +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 33dceef83f207..4b75833a1e972 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 e4f9acd19759c..fe8671ab90658 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -3,84 +3,81 @@ * 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 { 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'; - -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, - ) { } +import { OngoingRequestCanceller } from './cancellation'; +import { TypeScriptVersionManager } from './versionManager'; +import { TypeScriptVersion } from './versionProvider'; - 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; readonly onExit: vscode.Event; readonly onError: vscode.Event; - readonly onReaderError: vscode.Event; 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>; - executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | 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>; + executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise> | undefined; dispose(): void; } export interface TsServerDelegate { - onFatalError(command: string): void; + 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; private readonly _requestQueue = new RequestQueue(); private readonly _callbacks = new CallbackMap(); private readonly _pendingResponses = new Set(); 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(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()); 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,17 +131,24 @@ 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': const event = message as Proto.Event; if (event.event === 'requestCompleted') { const seq = (event as Proto.RequestCompletedEvent).body.request_seq; - const p = this._callbacks.fetch(seq); - if (p) { - this._tracer.traceRequestCompleted(this._serverId, 'requestCompleted', seq, p.startTime); - p.onSuccess(undefined); + const callback = this._callbacks.fetch(seq); + if (callback) { + this._tracer.traceRequestCompleted(this._serverId, 'requestCompleted', seq, callback); + callback.onSuccess(undefined); } } else { this._tracer.traceEvent(this._serverId, event); @@ -186,7 +191,7 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe return; } - this._tracer.traceResponse(this._serverId, response, callback.startTime); + this._tracer.traceResponse(this._serverId, response, callback); if (response.success) { callback.onSuccess(response); } else if (response.message === 'No content available.') { @@ -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>; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | 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>; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise> | 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> | undefined; if (executeInfo.expectsResult) { result = new Promise>((resolve, reject) => { - this._callbacks.add(request.seq, { onSuccess: resolve, onError: reject, startTime: Date.now(), isAsync: executeInfo.isAsync }, executeInfo.isAsync); + this._callbacks.add(request.seq, { onSuccess: resolve, onError: reject, queuingStartTime: Date.now(), isAsync: executeInfo.isAsync }, executeInfo.isAsync); if (executeInfo.token) { executeInfo.token.onCancellationRequested(() => { @@ -222,21 +227,13 @@ export class ProcessBasedTsServer extends Disposable implements ITypeScriptServe if (!executeInfo.token || !executeInfo.token.isCancellationRequested) { /* __GDPR__ "languageServiceErrorResponse" : { - "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "stack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "errortext" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, "${include}": [ - "${TypeScriptCommonProperties}" + "${TypeScriptCommonProperties}", + "${TypeScriptRequestErrorProperties}" ] } */ - this._telemetryReporter.logTelemetry('languageServiceErrorResponse', { - command: err.serverCommand, - message: err.serverMessage || '', - stack: err.serverStack || '', - errortext: err.serverErrorText || '', - }); + this._telemetryReporter.logTelemetry('languageServiceErrorResponse', err.telemetry); } } @@ -305,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([ + '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> | 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> | 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([ + 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.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(semanticServer.onExit(e => { + this._register(this.mainServer.onExit(e => { this._onExit.fire(e); - this.syntaxServer.kill(); + this.getErrServer.kill(); })); - this._register(semanticServer.onError(e => this._onError.fire(e))); } private readonly _onEvent = this._register(new vscode.EventEmitter()); @@ -332,16 +453,27 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ private readonly _onError = this._register(new vscode.EventEmitter()); 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([ + 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>; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise> | 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([ 'navtree', 'getOutliningSpans', 'jsxClosingTag', @@ -350,86 +482,147 @@ export class SyntaxRoutingTsServer extends Disposable implements ITypeScriptServ 'formatonkey', 'docCommentTemplate', ]); - private static readonly sharedCommands = new Set([ - 'change', - 'close', - 'open', - 'updateOpen', - 'configure', + + /** + * Commands that should always be run on the semantic server. + */ + private static readonly semanticCommands = new Set([ + '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>; - public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | 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([ + 'completions', + 'completionEntryDetails', + 'completionInfo', + 'definition', + 'definitionAndBoundSpan', + 'documentHighlights', + 'implementation', + 'navto', + 'quickinfo', + 'references', + 'rename', + 'signatureHelp', + ]); - const enum RequestState { Unresolved, Resolved, Errored } - let syntaxRequestState = RequestState.Unresolved; - let semanticRequestState = 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; - } - source.cancel(); - }); - token = source.token; - } + private _projectLoading = true; - const semanticRequest = this.semanticServer.executeImpl(command, args, { ...executeInfo, token }); - if (semanticRequest) { - semanticRequest - .then(result => { - semanticRequestState = RequestState.Resolved; - if (syntaxRequestState === RequestState.Errored) { - // We've gone out of sync - this._delegate.onFatalError(command); + 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; } - return result; - }, err => { - semanticRequestState = RequestState.Errored; - if (syntaxRequestState === RequestState.Resolved) { - // We've gone out of sync - this._delegate.onFatalError(command); + + if (SyntaxRoutingTsServer.syntaxAlwaysCommands.has(command)) { + return true; } - throw err; - }); - } - const syntaxRequest = this.syntaxServer.executeImpl(command, args, { ...executeInfo, token }); - if (syntaxRequest) { - syntaxRequest - .then(result => { - syntaxRequestState = RequestState.Resolved; - if (semanticRequestState === RequestState.Errored) { - // We've gone out of sync - this._delegate.onFatalError(command); + if (SyntaxRoutingTsServer.semanticCommands.has(command)) { + return false; } - return result; - }, err => { - syntaxRequestState = RequestState.Errored; - if (semanticRequestState === RequestState.Resolved) { - // We've gone out of sync - this._delegate.onFatalError(command); + if (enableDynamicRouting && this.projectLoading && SyntaxRoutingTsServer.syntaxAllowedCommands.has(command)) { + return true; } - throw err; - }); + return false; + } + }, { + server: this.semanticServer, + canRun: undefined /* gets all other commands */ + } + ], + delegate); + + 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; } - 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()); + public readonly onEvent = this._onEvent.event; + + private readonly _onExit = this._register(new vscode.EventEmitter()); + public readonly onExit = this._onExit.event; + + private readonly _onError = this._register(new vscode.EventEmitter()); + 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>; + public executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, executionTarget?: ExectuionTarget }): Promise> | undefined { + return this.router.execute(command, args, executeInfo); + } +} + +namespace RequestState { + export const enum Type { Unresolved, Resolved, Errored } + + export const Unresolved = { type: Type.Unresolved } as const; + + export const Resolved = { type: Type.Resolved } as const; + + export class Errored { + readonly type = Type.Errored; + + constructor( + public readonly err: Error + ) { } + } + + export type State = typeof Unresolved | typeof Resolved | Errored; } diff --git a/extensions/typescript-language-features/src/tsServer/serverError.ts b/extensions/typescript-language-features/src/tsServer/serverError.ts index 7f58aa6b0f5a6..2653360c9be6e 100644 --- a/extensions/typescript-language-features/src/tsServer/serverError.ts +++ b/extensions/typescript-language-features/src/tsServer/serverError.ts @@ -3,9 +3,9 @@ * 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 { public static create( @@ -13,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}`); } @@ -31,31 +32,41 @@ 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" }, + "serverid" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "sanitizedstack" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + return { + command: this.serverCommand, + serverid: this.serverId, + sanitizedstack: this.sanitizedStack || '', + } as const; + } + /** * 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) }; } } @@ -64,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 0000000000000..01071f6e10e2a --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.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 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); + } + + 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/tsServer/serverProcess.electron.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts new file mode 100644 index 0000000000000..96b0c6b411e6f --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts @@ -0,0 +1,242 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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 * 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: '; +const contentLengthSize: number = Buffer.byteLength(contentLength, 'utf8'); +const blank: number = Buffer.from(' ', 'utf8')[0]; +const backslashR: number = Buffer.from('\r', 'utf8')[0]; +const backslashN: number = Buffer.from('\n', 'utf8')[0]; + +class ProtocolBuffer { + + private index: number = 0; + private buffer: Buffer = Buffer.allocUnsafe(defaultSize); + + public append(data: string | Buffer): void { + let toAppend: Buffer | null = null; + if (Buffer.isBuffer(data)) { + toAppend = data; + } else { + toAppend = Buffer.from(data, 'utf8'); + } + if (this.buffer.length - this.index >= toAppend.length) { + toAppend.copy(this.buffer, this.index, 0, toAppend.length); + } else { + let newSize = (Math.ceil((this.index + toAppend.length) / defaultSize) + 1) * defaultSize; + if (this.index === 0) { + this.buffer = Buffer.allocUnsafe(newSize); + toAppend.copy(this.buffer, 0, 0, toAppend.length); + } else { + this.buffer = Buffer.concat([this.buffer.slice(0, this.index), toAppend], newSize); + } + } + this.index += toAppend.length; + } + + public tryReadContentLength(): number { + let result = -1; + let current = 0; + // we are utf8 encoding... + while (current < this.index && (this.buffer[current] === blank || this.buffer[current] === backslashR || this.buffer[current] === backslashN)) { + current++; + } + if (this.index < current + contentLengthSize) { + return result; + } + current += contentLengthSize; + let start = current; + while (current < this.index && this.buffer[current] !== backslashR) { + current++; + } + if (current + 3 >= this.index || this.buffer[current + 1] !== backslashN || this.buffer[current + 2] !== backslashR || this.buffer[current + 3] !== backslashN) { + return result; + } + let data = this.buffer.toString('utf8', start, current); + result = parseInt(data); + this.buffer = this.buffer.slice(current + 4); + this.index = this.index - (current + 4); + return result; + } + + public tryReadContent(length: number): string | null { + if (this.index < length) { + return null; + } + let result = this.buffer.toString('utf8', 0, length); + let sourceStart = length; + while (sourceStart < this.index && (this.buffer[sourceStart] === backslashR || this.buffer[sourceStart] === backslashN)) { + sourceStart++; + } + this.buffer.copy(this.buffer, 0, sourceStart); + this.index = this.index - sourceStart; + return result; + } +} + +class Reader extends Disposable { + + private readonly buffer: ProtocolBuffer = new ProtocolBuffer(); + private nextMessageLength: number = -1; + + public constructor(readable: Readable) { + super(); + readable.on('data', data => this.onLengthData(data)); + } + + private readonly _onError = this._register(new vscode.EventEmitter()); + public readonly onError = this._onError.event; + + private readonly _onData = this._register(new vscode.EventEmitter()); + public readonly onData = this._onData.event; + + private onLengthData(data: Buffer | string): void { + if (this.isDisposed) { + return; + } + + try { + this.buffer.append(data); + while (true) { + if (this.nextMessageLength === -1) { + this.nextMessageLength = this.buffer.tryReadContentLength(); + if (this.nextMessageLength === -1) { + return; + } + } + const msg = this.buffer.tryReadContent(this.nextMessageLength); + if (msg === null) { + return; + } + this.nextMessageLength = -1; + const json = JSON.parse(msg); + this._onData.fire(json); + } + } catch (e) { + this._onError.fire(e); + } + } +} + +export class ChildServerProcess extends Disposable implements TsServerProcess { + private readonly _reader: Reader; + + 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(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 171f204703eaf..7f4aaeefe13e7 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -3,66 +3,131 @@ * 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 { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider'; import { PluginManager } from '../utils/plugins'; -import TelemetryReporter from '../utils/telemetry'; +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 +138,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,16 +190,17 @@ 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 (cancellationPipeName) { + args.push('--cancellationPipeName', cancellationPipeName + '*'); + } if (TypeScriptServerSpawner.isLoggingEnabled(configuration)) { const logDir = this._logDirectoryProvider.getNewLogDirectory(); @@ -170,22 +244,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 +258,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 0000000000000..4811fefd3a7cf --- /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()); + 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 { + const selected = await vscode.window.showQuickPick([ + 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 { + 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(useWorkspaceTsdkStorageKey, false); + } + + private get suppressPromptWorkspaceTsdkSetting(): boolean { + return this.workspaceState.get(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/tsServer/versionProvider.electron.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts new file mode 100644 index 0000000000000..760f82d7b09af --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * 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 API from '../utils/api'; +import { TypeScriptServiceConfiguration } from '../utils/configuration'; +import { RelativeWorkspacePathResolver } from '../utils/relativePathResolver'; +import { ITypeScriptVersionProvider, localize, TypeScriptVersion, TypeScriptVersionSource } from './versionProvider'; + +export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider { + + 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(); + 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; + } + + // Allow TS developers to provide custom version + const tsdkVersion = vscode.workspace.getConfiguration().get('typescript.tsdk_version', undefined); + if (tsdkVersion) { + return API.fromVersionString(tsdkVersion); + } + + return undefined; + } + + private static getTypeScriptVersion(serverPath: string): API | undefined { + if (!fs.existsSync(serverPath)) { + return undefined; + } + + const p = serverPath.split(path.sep); + if (p.length <= 2) { + return undefined; + } + const p2 = p.slice(0, -2); + const modulePath = p2.join(path.sep); + let fileName = path.join(modulePath, 'package.json'); + if (!fs.existsSync(fileName)) { + // Special case for ts dev versions + if (path.basename(modulePath) === 'built') { + fileName = path.join(modulePath, '..', 'package.json'); + } + } + if (!fs.existsSync(fileName)) { + return undefined; + } + + const contents = fs.readFileSync(fileName).toString(); + let desc: any = null; + try { + desc = JSON.parse(contents); + } catch (err) { + return undefined; + } + if (!desc || !desc.version) { + return undefined; + } + return desc.version ? API.fromVersionString(desc.version) : undefined; + } +} 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 0000000000000..43f16c7c19d9f --- /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 0000000000000..20b9debc27ba4 --- /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 { + const info = this._delegate(); + + + const result = await vscode.window.showQuickPick(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 7e0d159132c60..908735a7f2dfc 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -9,71 +9,81 @@ * ------------------------------------------------------------------------------------------ */ 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 } 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(); - 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(); - 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), @@ -182,15 +208,10 @@ export default class TypeScriptServiceClientHost extends Disposable { private populateService(): void { this.fileConfigurationManager.reset(); - this.client.bufferSyncSupport.reOpenDocuments(); - this.client.bufferSyncSupport.requestAllDiagnostics(); - // 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( @@ -231,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)); @@ -245,19 +266,27 @@ export default class TypeScriptServiceClientHost extends Disposable { } const relatedInformation = diagnostic.relatedInformation; if (relatedInformation) { - converted.relatedInformation = relatedInformation.map((info: any) => { - let span = info.span; + converted.relatedInformation = coalesce(relatedInformation.map((info: any) => { + const span = info.span; if (!span) { return undefined; } return new vscode.DiagnosticRelatedInformation(typeConverters.Location.fromTextSpan(this.client.toResource(span.file), span), info.message); - }).filter((x: any) => !!x) as vscode.DiagnosticRelatedInformation[]; + })); } + const tags: vscode.DiagnosticTag[] = []; if (diagnostic.reportsUnnecessary) { - converted.tags = [vscode.DiagnosticTag.Unnecessary]; + tags.push(vscode.DiagnosticTag.Unnecessary); + } + if (diagnostic.reportsDeprecated) { + tags.push(vscode.DiagnosticTag.Deprecated); } - (converted as vscode.Diagnostic & { reportUnnecessary: any }).reportUnnecessary = diagnostic.reportsUnnecessary; - return converted as vscode.Diagnostic & { reportUnnecessary: any }; + 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 { @@ -284,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 741fee0e94bef..58a78ad19b58c 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 { @@ -21,7 +28,7 @@ export namespace ServerResponse { ) { } } - export const NoContent = new class { readonly type = 'noContent'; }; + export const NoContent = { type: 'noContent' } as const; export type Response = T | Cancelled | typeof NoContent; } @@ -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]; @@ -58,6 +65,9 @@ interface StandardTsServerRequests { 'signatureHelp': [Proto.SignatureHelpRequestArgs, Proto.SignatureHelpResponse]; 'typeDefinition': [Proto.FileLocationRequestArgs, Proto.TypeDefinitionResponse]; 'updateOpen': [Proto.UpdateOpenRequestArgs, Proto.Response]; + 'prepareCallHierarchy': [Proto.FileLocationRequestArgs, Proto.PrepareCallHierarchyResponse]; + 'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse]; + 'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse]; } interface NoResponseTsServerRequests { @@ -71,6 +81,7 @@ interface NoResponseTsServerRequests { interface AsyncTsServerRequests { 'geterr': [Proto.GeterrRequestArgs, Proto.Response]; + 'geterrForProject': [Proto.GeterrForProjectRequestArgs, Proto.Response]; } export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRequests & AsyncTsServerRequests; @@ -78,8 +89,39 @@ export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRe export type ExecConfig = { readonly lowPriority?: boolean; readonly nonRecoverable?: boolean; + 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; + + 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). @@ -103,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; + readonly onTsServerStarted: vscode.Event<{ version: TypeScriptVersion, usedApiVersion: API }>; readonly onProjectLanguageServiceStateChanged: vscode.Event; readonly onDidBeginInstallTypings: vscode.Event; readonly onDidEndInstallTypings: vscode.Event; readonly onTypesInstallerInitializationFailed: vscode.Event; + readonly capabilities: ClientCapabilities; + readonly onDidChangeCapabilities: vscode.Event; + + onReady(f: () => void): Promise; + + showVersionPicker(): void; + readonly apiVersion: API; + readonly pluginManager: PluginManager; readonly configuration: TypeScriptServiceConfiguration; - readonly logger: Logger; readonly bufferSyncSupport: BufferSyncSupport; + readonly telemetryReporter: TelemetryReporter; execute( command: K, @@ -133,7 +188,11 @@ export interface ITypeScriptServiceClient { args: NoResponseTsServerRequests[K][0] ): void; - executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise>; + executeAsync( + command: K, + args: AsyncTsServerRequests[K][0], + token: vscode.CancellationToken + ): Promise>; /** * 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 e1a2b10270f6b..c068f94189b23 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -3,29 +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 { ITypeScriptServiceClient, ServerResponse, TypeScriptRequests, ExecConfig } from './typescriptService'; +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 { 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 } from './utils/telemetry'; +import { TelemetryProperties, TelemetryReporter, VSCodeTelemetryReporter } from './utils/telemetry'; import Tracer from './utils/tracer'; -import { inferredProjectConfig } from './utils/tsconfig'; -import { TypeScriptVersionPicker } from './utils/versionPicker'; -import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider'; -import { TypeScriptServerSpawner } from './tsServer/spawner'; +import { inferredProjectCompilerOptions, ProjectType } from './utils/tsconfig'; const localize = nls.loadMessageBundle(); @@ -35,6 +38,11 @@ export interface TsDiagnostics { readonly diagnostics: Proto.Diagnostic[]; } +interface ToCancelOnResourceChanged { + readonly resource: vscode.Uri; + cancel(): void; +} + namespace ServerState { export const enum Type { None, @@ -42,10 +50,11 @@ namespace ServerState { Errored } - export const None = new class { readonly type = Type.None; }; + export const None = { type: Type.None } as const; export class Running { readonly type = Type.Running; + constructor( public readonly server: ITypeScriptServer, @@ -58,14 +67,25 @@ namespace ServerState { * Version reported by currently-running tsserver. */ public tsserverVersion: string | undefined, - public langaugeServiceEnabled: boolean, + public languageServiceEnabled: boolean, ) { } + + public readonly toCancelOnResourceChange = new Set(); + + updateTsserverVersion(tsserverVersion: string) { + this.tsserverVersion = tsserverVersion; + } + + updateLanguageServiceEnabled(enabled: boolean) { + this.languageServiceEnabled = enabled; + } } export class Errored { readonly type = Type.Errored; constructor( public readonly error: Error, + public readonly tsServerLogFile: string | undefined, ) { } } @@ -73,70 +93,98 @@ namespace ServerState { } export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient { - private static readonly WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`; - private pathSeparator: string; + private readonly pathSeparator: string; + private readonly inMemoryResourcePrefix = '^'; private _onReady?: { promise: Promise; 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(); - // tslint:disable-next-line: no-var-keyword - var p = new Promise((resolve, reject) => { - this._onReady = { promise: p, resolve, reject }; + let resolve: () => void; + let reject: () => void; + const p = new Promise((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.pluginPathsProvider = new TypeScriptPluginPathsProvider(this._configuration); - this.versionPicker = new TypeScriptVersionPicker(this.versionProvider, this.workspaceState); + this.versionProvider.updateConfiguration(this._configuration); - this.tracer = new Tracer(this.logger); + this.pluginPathsProvider = new TypeScriptPluginPathsProvider(this._configuration); + this._versionManager = this._register(new TypeScriptVersionManager(this._configuration, this.versionProvider, this.workspaceState)); + this._register(this._versionManager.onDidPickNewVersion(() => { + this.restartTsServer(); + })); - 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); }, null, this._disposables); + this.bufferSyncSupport.onWillChange(resource => { + this.cancelInflightRequestsForResource(resource); + }); + vscode.workspace.onDidChangeConfiguration(() => { const oldConfiguration = this._configuration; this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); this.versionProvider.updateConfiguration(this._configuration); + this._versionManager.updateConfiguration(this._configuration); this.pluginPathsProvider.updateConfiguration(this._configuration); this.tracer.updateConfiguration(); @@ -159,10 +207,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType return this.serverState.tsserverVersion; } } - return this.apiVersion.version; + 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); @@ -173,6 +221,40 @@ 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()); + readonly onDidChangeCapabilities = this._onDidChangeCapabilities.event; + + private cancelInflightRequestsForResource(resource: vscode.Uri): void { + if (this.serverState.type !== ServerState.Type.Running) { + return; + } + + for (const request of this.serverState.toCancelOnResourceChange) { + if (request.resource.toString() === resource.toString()) { + request.cancel(); + } + } + } + public get configuration() { return this._configuration; } @@ -199,7 +281,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.serverState = this.startService(true); } - private readonly _onTsServerStarted = this._register(new vscode.EventEmitter()); + 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()); @@ -245,7 +327,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.logger.error(message, data); } - private logTelemetry(eventName: string, properties?: { readonly [prop: string]: string }) { + private logTelemetry(eventName: string, properties?: TelemetryProperties) { this.telemetryReporter.logTelemetry(eventName, properties); } @@ -260,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() { @@ -271,25 +353,32 @@ 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; + if (this.hasServerFatallyCrashedTooManyTimes) { + this.info(`Not starting server. Too many crashes.`); + return ServerState.None; + } - 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)); + 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.versionPicker.useBundledVersion(); - currentVersion = this.versionPicker.currentVersion; + this._versionManager.reset(); + version = this._versionManager.currentVersion; } - const apiVersion = this.versionPicker.currentVersion.apiVersion || API.defaultVersion; - this.onDidChangeTypeScriptVersion(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, { - onFatalError: (command) => this.fatalError(command), + 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); this.lastStart = Date.now(); @@ -305,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) => { @@ -318,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}`); @@ -344,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}" ] @@ -363,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 { - return this.showVersionPicker(false); - } - - private showVersionPicker(firstRun: boolean): Thenable { - 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 { + this._versionManager.promptUserForVersion(); } public async openTsServerLogFile(): Promise { @@ -440,17 +521,27 @@ 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); if (resendModels) { this._onResendModelsRequested.fire(); + this.bufferSyncSupport.reinitialize(); + this.bufferSyncSupport.requestAllDiagnostics(); } // Reconfigure any plugins @@ -468,41 +559,40 @@ export default class TypeScriptServiceClient extends Disposable implements IType private getCompilerOptionsForInferredProjects(configuration: TypeScriptServiceConfiguration): Proto.ExternalProjectCompilerOptions { return { - ...inferredProjectConfig(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 = undefined; + if (this.numberRestarts > 5) { - let prompt: Thenable | undefined = undefined; this.numberRestarts = 0; if (diff < 10 * 1000 /* 10 seconds */) { this.lastStart = Date.now(); startService = false; - prompt = vscode.window.showErrorMessage( + 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}": [ @@ -513,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( + 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.reportIssues'); - } - 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); } @@ -536,23 +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; } - const 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); - // Both \ and / must be escaped in regular expressions - return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); + // Both \ and / must be escaped in regular expressions + return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); + } + default: + { + return this.inMemoryResourcePrefix + resource.toString(true); + } + } } public toPath(resource: vscode.Uri): string | undefined { @@ -561,37 +665,44 @@ 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; } - private get inMemoryResourcePrefix(): string { - return this.apiVersion.gte(API.v270) ? '^' : ''; + public hasCapabilityForResource(resource: vscode.Uri, capability: ClientCapability): boolean { + switch (capability) { + case ClientCapability.Semantic: + { + switch (resource.scheme) { + case fileSchemes.file: + case fileSchemes.untitled: + return true; + default: + return false; + } + } + case ClientCapability.Syntax: + case ClientCapability.EnhancedSyntax: + { + return true; + } + } } 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); - if (this.inMemoryResourcePrefix) { - 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)) - }); - } - } + 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; } @@ -609,15 +720,40 @@ export default class TypeScriptServiceClient extends Disposable implements IType } public execute(command: keyof TypeScriptRequests, args: any, token: vscode.CancellationToken, config?: ExecConfig): Promise> { - const execution = this.executeImpl(command, args, { - isAsync: false, - token, - expectsResult: true, - lowPriority: config?.lowPriority - }); + let execution: Promise>; + + if (config?.cancelOnResourceChange) { + const runningServerState = this.service(); + + const source = new vscode.CancellationTokenSource(); + token.onCancellationRequested(() => source.cancel()); + + const inFlight: ToCancelOnResourceChanged = { + resource: config.cancelOnResourceChange, + cancel: () => source.cancel(), + }; + runningServerState.toCancelOnResourceChange.add(inFlight); + + execution = this.executeImpl(command, args, { + isAsync: false, + token: source.token, + expectsResult: true, + ...config, + }).finally(() => { + runningServerState.toCancelOnResourceChange.delete(inFlight); + source.dispose(); + }); + } else { + execution = this.executeImpl(command, args, { + isAsync: false, + token, + expectsResult: true, + ...config, + }); + } if (config?.nonRecoverable) { - execution.catch(() => this.fatalError(command)); + execution.catch(err => this.fatalError(command, err)); } return execution; @@ -639,9 +775,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>; - private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise> | 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>; + private executeImpl(command: keyof TypeScriptRequests, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean, requireSemantic?: boolean }): Promise> | undefined { this.bufferSyncSupport.beforeCommand(command); const runningServerState = this.service(); return runningServerState.server.executeImpl(command, args, executeInfo); @@ -651,29 +787,37 @@ export default class TypeScriptServiceClient extends Disposable implements IType return this.bufferSyncSupport.interuptGetErr(f); } - private fatalError(command: string): void { + private fatalError(command: string, error: unknown): void { /* __GDPR__ "fatalError" : { "${include}": [ "${TypeScriptCommonProperties}", - "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - ] + "${TypeScriptRequestErrorProperties}" + ], + "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - this.logTelemetry('fatalError', { command }); + 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); + } 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(); @@ -687,55 +831,52 @@ 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 = { - ...this.serverState, - langaugeServiceEnabled: body.languageServiceEnabled, - }; + this.serverState.updateLanguageServiceEnabled(body.languageServiceEnabled); } this._onProjectLanguageServiceStateChanged.fire(body); break; } - case 'projectsUpdatedInBackground': + case EventName.projectsUpdatedInBackground: 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; } @@ -773,10 +914,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } if (telemetryData.telemetryEventName === 'projectInfo') { if (this.serverState.type === ServerState.Type.Running) { - this.serverState = { - ...this.serverState, - tsserverVersion: properties['version'] - }; + this.serverState.updateTsserverVersion(properties['version']); } } @@ -801,6 +939,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; diff --git a/extensions/typescript-language-features/src/typings/ref.d.ts b/extensions/typescript-language-features/src/typings/ref.d.ts index c9849d48e083f..43a9cadf25022 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. *--------------------------------------------------------------------------------------------*/ -/// /// diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index c30bee16c7e1c..289c091f5b107 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -10,7 +10,7 @@ const localize = nls.loadMessageBundle(); export default class API { private static fromSimpleString(value: string): API { - return new API(value, value); + return new API(value, value, value); } public static readonly defaultVersion = API.fromSimpleString('1.0.0'); @@ -31,11 +31,16 @@ export default class API { public static readonly v340 = API.fromSimpleString('3.4.0'); 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); if (!version) { - return new API(localize('invalidVersion', 'invalid version'), '1.0.0'); + return new API(localize('invalidVersion', 'invalid version'), '1.0.0', '1.0.0'); } // Cut off any prerelease tag since we sometimes consume those on purpose. @@ -43,14 +48,30 @@ export default class API { if (index >= 0) { version = version.substr(0, index); } - return new API(versionString, version); + return new API(versionString, version, versionString); } private constructor( + /** + * Human readable string for the current version. Displayed in the UI + */ public readonly displayName: string, - public readonly version: string + + /** + * Semver version, e.g. '3.9.0' + */ + public readonly version: string, + + /** + * Full version string including pre-release tags, e.g. '3.9.0-beta' + */ + 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/arrays.ts b/extensions/typescript-language-features/src/utils/arrays.ts index f9435fe9b2002..3619d828dc35e 100644 --- a/extensions/typescript-language-features/src/utils/arrays.ts +++ b/extensions/typescript-language-features/src/utils/arrays.ts @@ -19,6 +19,10 @@ export function equals( return a.every((x, i) => itemEquals(x, b[i])); } -export function flatten(arr: ReadonlyArray[]): T[] { - return Array.prototype.concat.apply([], arr); -} \ No newline at end of file +export function flatten(array: ReadonlyArray[]): T[] { + return Array.prototype.concat.apply([], array); +} + +export function coalesce(array: ReadonlyArray): T[] { + return array.filter(e => !!e); +} diff --git a/extensions/typescript-language-features/src/utils/cancellation.ts b/extensions/typescript-language-features/src/utils/cancellation.ts index 10933baa9393d..72672663ad275 100644 --- a/extensions/typescript-language-features/src/utils/cancellation.ts +++ b/extensions/typescript-language-features/src/utils/cancellation.ts @@ -5,6 +5,9 @@ import * as vscode from 'vscode'; -const nulTokenSource = new vscode.CancellationTokenSource(); +const noopDisposable = vscode.Disposable.from(); -export const nulToken = nulTokenSource.token; \ No newline at end of file +export const nulToken: vscode.CancellationToken = { + isCancellationRequested: false, + onCancellationRequested: () => noopDisposable +}; diff --git a/extensions/typescript-language-features/src/utils/codeAction.ts b/extensions/typescript-language-features/src/utils/codeAction.ts index c45379e611ae4..df345e3351771 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 54c3a19b00141..f549ff6f9e255 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('typescript.locale', null); } - private static readUseSeparateSyntaxServer(configuration: vscode.WorkspaceConfiguration): boolean { - return configuration.get('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('typescript.tsserver.experimental.enableProjectDiagnostics', false); + } + + private static readWatchOptions(configuration: vscode.WorkspaceConfiguration): protocol.WatchOptions | undefined { + return configuration.get('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('typescript.enablePromptUseWorkspaceTsdk', false); + } } diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 434379f57e18e..29597ab50cb67 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()); + public readonly onDidChange = this._onDidChange.event; +} + +class ConditionalRegistration { private registration: vscode.Disposable | undefined = undefined; public constructor( - private readonly _doRegister: () => vscode.Disposable - ) { } + private readonly conditions: readonly Condition[], + private readonly doRegister: () => vscode.Disposable + ) { + for (const condition of conditions) { + condition.onDidChange(() => this.update()); + } + this.update(); + } public dispose() { - if (this.registration) { - this.registration.dispose(); - this.registration = undefined; - } + this.registration?.dispose(); + this.registration = undefined; } - public update(enabled: boolean) { + 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 function requireMinVersion( + client: ITypeScriptServiceClient, + minVersion: API, +) { + return new Condition( + () => client.apiVersion.gte(minVersion), + client.onTsServerStarted + ); +} -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(); - } +export function requireConfiguration( + language: string, + configValue: string, +) { + return new Condition( + () => { + const config = vscode.workspace.getConfiguration(language, null); + return !!config.get(configValue); + }, + vscode.workspace.onDidChangeConfiguration + ); +} - private update() { - const config = vscode.workspace.getConfiguration(this.language, null); - this._registration.update(!!config.get(this.configValue)); - } +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 0000000000000..0574c3f587df5 --- /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: vscode.DocumentFilter[]; + + /** + * Selector for files which require semantic server support. + */ + readonly semantic: 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 ea16bae42773f..0000000000000 --- 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 0000000000000..2f554e9658468 --- /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 6efcfab4d3b71..d465a60326e02 100644 --- a/extensions/typescript-language-features/src/utils/fileSchemes.ts +++ b/extensions/typescript-language-features/src/utils/fileSchemes.ts @@ -6,14 +6,19 @@ 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 ]; -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 0000000000000..3a6711224e2a4 --- /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 0000000000000..98e47f0ebb2bb --- /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/largeProjectStatus.ts b/extensions/typescript-language-features/src/utils/largeProjectStatus.ts new file mode 100644 index 0000000000000..223d7cb471671 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/largeProjectStatus.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { loadMessageBundle } from 'vscode-nls'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import { TelemetryReporter } from './telemetry'; +import { isImplicitProjectConfigFile, openOrCreateConfig, ProjectType } from './tsconfig'; + +const localize = loadMessageBundle(); + +interface Hint { + message: string; +} + +class ExcludeHintItem { + public configFileName?: string; + private _item: vscode.StatusBarItem; + private _currentHint?: Hint; + + constructor( + private readonly telemetryReporter: TelemetryReporter + ) { + this._item = vscode.window.createStatusBarItem({ + id: 'status.typescript.exclude', + name: localize('statusExclude', "TypeScript: Configure Excludes"), + alignment: vscode.StatusBarAlignment.Right, + priority: 98 /* to the right of typescript version status (99) */ + }); + this._item.command = 'js.projectStatus.command'; + } + + public getCurrentHint(): Hint { + return this._currentHint!; + } + + public hide() { + this._item.hide(); + } + + public show(largeRoots?: string) { + this._currentHint = { + message: largeRoots + ? localize('hintExclude', "To enable project-wide JavaScript/TypeScript language features, exclude folders with many files, like: {0}", largeRoots) + : localize('hintExclude.generic', "To enable project-wide JavaScript/TypeScript language features, exclude large folders with source files that you do not work on.") + }; + this._item.tooltip = this._currentHint.message; + this._item.text = localize('large.label', "Configure Excludes"); + this._item.tooltip = localize('hintExclude.tooltip', "To enable project-wide JavaScript/TypeScript language features, exclude large folders with source files that you do not work on."); + this._item.color = '#A5DF3B'; + this._item.show(); + /* __GDPR__ + "js.hintProjectExcludes" : { + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('js.hintProjectExcludes'); + } +} + + +function createLargeProjectMonitorFromTypeScript(item: ExcludeHintItem, client: ITypeScriptServiceClient): vscode.Disposable { + + interface LargeProjectMessageItem extends vscode.MessageItem { + index: number; + } + + return client.onProjectLanguageServiceStateChanged(body => { + if (body.languageServiceEnabled) { + item.hide(); + } else { + item.show(); + const configFileName = body.projectName; + if (configFileName) { + item.configFileName = configFileName; + vscode.window.showWarningMessage(item.getCurrentHint().message, + { + title: localize('large.label', "Configure Excludes"), + index: 0 + }).then(selected => { + if (selected && selected.index === 0) { + onConfigureExcludesSelected(client, configFileName); + } + }); + } + } + }); +} + +function onConfigureExcludesSelected( + client: ITypeScriptServiceClient, + configFileName: string +) { + if (!isImplicitProjectConfigFile(configFileName)) { + vscode.workspace.openTextDocument(configFileName) + .then(vscode.window.showTextDocument); + } else { + const root = client.getWorkspaceRootForResource(vscode.Uri.file(configFileName)); + if (root) { + openOrCreateConfig( + /tsconfig\.?.*\.json/.test(configFileName) ? ProjectType.TypeScript : ProjectType.JavaScript, + root, + client.configuration); + } + } +} + +export function create( + client: ITypeScriptServiceClient, +): vscode.Disposable { + const toDispose: vscode.Disposable[] = []; + + const item = new ExcludeHintItem(client.telemetryReporter); + toDispose.push(vscode.commands.registerCommand('js.projectStatus.command', () => { + if (item.configFileName) { + onConfigureExcludesSelected(client, item.configFileName); + } + const { message } = item.getCurrentHint(); + return vscode.window.showInformationMessage(message); + })); + + toDispose.push(createLargeProjectMonitorFromTypeScript(item, client)); + + return vscode.Disposable.from(...toDispose); +} diff --git a/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts b/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts deleted file mode 100644 index af6886e704300..0000000000000 --- a/extensions/typescript-language-features/src/utils/logDirectoryProvider.ts +++ /dev/null @@ -1,40 +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 * as path from 'path'; -import * as vscode from 'vscode'; -import { memoize } from './memoize'; - -export default class LogDirectoryProvider { - public constructor( - private readonly context: vscode.ExtensionContext - ) { } - - public getNewLogDirectory(): string | undefined { - const root = this.logDirectory(); - if (root) { - try { - return fs.mkdtempSync(path.join(root, `tsserver-log-`)); - } catch (e) { - return undefined; - } - } - return undefined; - } - - @memoize - private logDirectory(): string | undefined { - try { - const path = this.context.logPath; - if (!fs.existsSync(path)) { - fs.mkdirSync(path); - } - return this.context.logPath; - } catch { - return undefined; - } - } -} diff --git a/extensions/typescript-language-features/src/utils/logger.ts b/extensions/typescript-language-features/src/utils/logger.ts index 991e9d52be373..74b9fbcbf087e 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 { @@ -41,9 +41,20 @@ export default class Logger { } public logLevel(level: LogLevel, message: string, data?: any): void { - this.output.appendLine(`[${level} - ${(new Date().toLocaleTimeString())}] ${message}`); + this.output.appendLine(`[${level} - ${this.now()}] ${message}`); if (data) { this.output.appendLine(this.data2String(data)); } } -} \ No newline at end of file + + 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; +} 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 0000000000000..589b5da3d52c9 --- /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 { + 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 0000000000000..a31467bd8d664 --- /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 0000000000000..2d754bf405471 --- /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 71ce2430cb899..6d0708b15db74 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; + 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 2b2108ccd5778..3d059dca331ef 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) { @@ -28,26 +46,37 @@ function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined { } else { return makeCodeblock(tag.text); } + case 'author': + // fix obsucated email address, #80898 + const emailMatch = tag.text.match(/(.+)\s<([-.\w]+@[-.\w]+)>/); + if (emailMatch === null) { + return tag.text; + } else { + return `${emailMatch[1]} ${emailMatch[2]}`; + } case 'default': return makeCodeblock(tag.text); } - return tag.text; + return processInlineTags(tag.text); } function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined { switch (tag.name) { + case 'augments': + case 'extends': case 'param': - const body = (tag.text || '').split(/^([\w\.]+)\s*-?\s*/); - if (body && body.length === 3) { + case 'template': + 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)}`); } } @@ -60,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(); @@ -84,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/projectStatus.ts b/extensions/typescript-language-features/src/utils/projectStatus.ts deleted file mode 100644 index 15aa72a80d09c..0000000000000 --- a/extensions/typescript-language-features/src/utils/projectStatus.ts +++ /dev/null @@ -1,131 +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 { loadMessageBundle } from 'vscode-nls'; -import { ITypeScriptServiceClient } from '../typescriptService'; -import TelemetryReporter from './telemetry'; -import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './tsconfig'; - -const localize = loadMessageBundle(); - -interface Hint { - message: string; -} - -class ExcludeHintItem { - public configFileName?: string; - private _item: vscode.StatusBarItem; - private _currentHint?: Hint; - - constructor( - private readonly telemetryReporter: TelemetryReporter - ) { - this._item = vscode.window.createStatusBarItem({ - id: 'status.typescript.exclude', - name: localize('statusExclude', "TypeScript: Configure Excludes"), - alignment: vscode.StatusBarAlignment.Right, - priority: 98 /* to the right of typescript version status (99) */ - }); - this._item.command = 'js.projectStatus.command'; - } - - public getCurrentHint(): Hint { - return this._currentHint!; - } - - public hide() { - this._item.hide(); - } - - public show(largeRoots?: string) { - this._currentHint = { - message: largeRoots - ? localize('hintExclude', "To enable project-wide JavaScript/TypeScript language features, exclude folders with many files, like: {0}", largeRoots) - : localize('hintExclude.generic', "To enable project-wide JavaScript/TypeScript language features, exclude large folders with source files that you do not work on.") - }; - this._item.tooltip = this._currentHint.message; - this._item.text = localize('large.label', "Configure Excludes"); - this._item.tooltip = localize('hintExclude.tooltip', "To enable project-wide JavaScript/TypeScript language features, exclude large folders with source files that you do not work on."); - this._item.color = '#A5DF3B'; - this._item.show(); - /* __GDPR__ - "js.hintProjectExcludes" : { - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('js.hintProjectExcludes'); - } -} - - -function createLargeProjectMonitorFromTypeScript(item: ExcludeHintItem, client: ITypeScriptServiceClient): vscode.Disposable { - - interface LargeProjectMessageItem extends vscode.MessageItem { - index: number; - } - - return client.onProjectLanguageServiceStateChanged(body => { - if (body.languageServiceEnabled) { - item.hide(); - } else { - item.show(); - const configFileName = body.projectName; - if (configFileName) { - item.configFileName = configFileName; - vscode.window.showWarningMessage(item.getCurrentHint().message, - { - title: localize('large.label', "Configure Excludes"), - index: 0 - }).then(selected => { - if (selected && selected.index === 0) { - onConfigureExcludesSelected(client, configFileName); - } - }); - } - } - }); -} - -function onConfigureExcludesSelected( - client: ITypeScriptServiceClient, - configFileName: string -) { - if (!isImplicitProjectConfigFile(configFileName)) { - vscode.workspace.openTextDocument(configFileName) - .then(vscode.window.showTextDocument); - } else { - const root = client.getWorkspaceRootForResource(vscode.Uri.file(configFileName)); - if (root) { - openOrCreateConfigFile( - configFileName.match(/tsconfig\.?.*\.json/) !== null, - root, - client.configuration); - } - } -} - -export function create( - client: ITypeScriptServiceClient, - telemetryReporter: TelemetryReporter -) { - const toDispose: vscode.Disposable[] = []; - - const item = new ExcludeHintItem(telemetryReporter); - toDispose.push(vscode.commands.registerCommand('js.projectStatus.command', () => { - if (item.configFileName) { - onConfigureExcludesSelected(client, item.configFileName); - } - let { message } = item.getCurrentHint(); - return vscode.window.showInformationMessage(message); - })); - - toDispose.push(createLargeProjectMonitorFromTypeScript(item, client)); - - return vscode.Disposable.from(...toDispose); -} - diff --git a/extensions/typescript-language-features/src/utils/resourceMap.ts b/extensions/typescript-language-features/src/utils/resourceMap.ts index a10fbd91636a2..ea4889ded3503 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 { private readonly _map = new Map(); 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 { 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 { - return /^[a-zA-Z]:\\/.test(path); +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 fbf2868d101eb..e0185db581b41 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 a98e84f7c3351..0000000000000 --- 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 = [ - { - 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(this.isCompletedMementoKey); - } - - public open(): void { - this.markComplete(); - vscode.commands.executeCommand('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(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(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 { - return new Map( - 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 { - enum Choice { - GoToSurvey = 1, - RemindLater = 2, - NeverAgain = 3, - } - - interface MessageItem extends vscode.MessageItem { - readonly choice: Choice; - } - - const response = await vscode.window.showInformationMessage(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('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/telemetry.ts b/extensions/typescript-language-features/src/utils/telemetry.ts index e17e0188e6d91..13527cae12503 100644 --- a/extensions/typescript-language-features/src/utils/telemetry.ts +++ b/extensions/typescript-language-features/src/utils/telemetry.ts @@ -13,8 +13,12 @@ interface PackageInfo { readonly aiKey: string; } -export default interface TelemetryReporter { - logTelemetry(eventName: string, properties?: { readonly [prop: string]: string }): void; +export interface TelemetryProperties { + readonly [prop: string]: string | number | undefined; +} + +export interface TelemetryReporter { + logTelemetry(eventName: string, properties?: TelemetryProperties): void; dispose(): void; } @@ -26,22 +30,20 @@ export class VSCodeTelemetryReporter implements TelemetryReporter { private readonly clientVersionDelegate: () => string ) { } - public logTelemetry(eventName: string, properties?: { [prop: string]: string }) { + public logTelemetry(eventName: string, properties: { [prop: string]: string } = {}) { const reporter = this.reporter; - if (reporter) { - if (!properties) { - properties = {}; - } + if (!reporter) { + return; + } - /* __GDPR__FRAGMENT__ - "TypeScriptCommonProperties" : { - "version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - properties['version'] = this.clientVersionDelegate(); + /* __GDPR__FRAGMENT__ + "TypeScriptCommonProperties" : { + "version" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + properties['version'] = this.clientVersionDelegate(); - reporter.sendTelemetryEvent(eventName, properties); - } + reporter.sendTelemetryEvent(eventName, properties); } public dispose() { 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 0000000000000..cd79b36415d09 --- /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 2af5f1732b0f0..0000000000000 --- 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 f7ca0d674e3e9..445b828d0c650 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, @@ -29,6 +29,10 @@ namespace Trace { } } +interface RequestExecutionMetadata { + readonly queuingStartTime: number +} + export default class Tracer { private trace?: Trace; @@ -61,7 +65,7 @@ export default class Tracer { this.logTrace(serverId, `Sending request: ${request.command} (${request.seq}). Response expected: ${responseExpected ? 'yes' : 'no'}. Current queue length: ${queueLength}`, data); } - public traceResponse(serverId: string, response: Proto.Response, startTime: number): void { + public traceResponse(serverId: string, response: Proto.Response, meta: RequestExecutionMetadata): void { if (this.trace === Trace.Off) { return; } @@ -69,14 +73,14 @@ export default class Tracer { if (this.trace === Trace.Verbose && response.body) { data = `Result: ${JSON.stringify(response.body, null, 4)}`; } - this.logTrace(serverId, `Response received: ${response.command} (${response.request_seq}). Request took ${Date.now() - startTime} ms. Success: ${response.success} ${!response.success ? '. Message: ' + response.message : ''}`, data); + this.logTrace(serverId, `Response received: ${response.command} (${response.request_seq}). Request took ${Date.now() - meta.queuingStartTime} ms. Success: ${response.success} ${!response.success ? '. Message: ' + response.message : ''}`, data); } - public traceRequestCompleted(serverId: string, command: string, request_seq: number, startTime: number): any { + public traceRequestCompleted(serverId: string, command: string, request_seq: number, meta: RequestExecutionMetadata): any { if (this.trace === Trace.Off) { return; } - this.logTrace(serverId, `Async response received: ${command} (${request_seq}). Request took ${Date.now() - startTime} ms.`); + this.logTrace(serverId, `Async response received: ${command} (${request_seq}). Request took ${Date.now() - meta.queuingStartTime} ms.`); } public traceEvent(serverId: string, event: Proto.Event): void { @@ -95,4 +99,4 @@ export default class Tracer { this.logger.logLevel('Trace', `<${serverId}> ${message}`, data); } } -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/utils/tsconfig.ts b/extensions/typescript-language-features/src/utils/tsconfig.ts index 994292dd62108..f6802ca3cccc9 100644 --- a/extensions/typescript-language-features/src/utils/tsconfig.ts +++ b/extensions/typescript-language-features/src/utils/tsconfig.ts @@ -5,37 +5,56 @@ 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.indexOf('/dev/null/') === 0; + return configFileName.startsWith('/dev/null/'); } -export function inferredProjectConfig( - config: TypeScriptServiceConfiguration +export function inferredProjectCompilerOptions( + projectType: ProjectType, + serviceConfig: TypeScriptServiceConfiguration, ): Proto.ExternalProjectCompilerOptions { - const base: Proto.ExternalProjectCompilerOptions = { + const projectConfig: Proto.ExternalProjectCompilerOptions = { module: 'commonjs' as Proto.ModuleKind, target: 'es2016' as Proto.ScriptTarget, - jsx: 'preserve' as Proto.JsxEmit + jsx: 'preserve' as Proto.JsxEmit, }; - if (config.checkJs) { - base.checkJs = true; + if (serviceConfig.checkJs) { + projectConfig.checkJs = true; + if (projectType === ProjectType.TypeScript) { + projectConfig.allowJs = true; + } + } + + if (serviceConfig.experimentalDecorators) { + projectConfig.experimentalDecorators = true; } - if (config.experimentalDecorators) { - base.experimentalDecorators = true; + if (projectType === ProjectType.TypeScript) { + projectConfig.sourceMap = true; } - return base; + return projectConfig; } function inferredProjectConfigSnippet( + projectType: ProjectType, config: TypeScriptServiceConfiguration ) { - const baseConfig = inferredProjectConfig(config); + const baseConfig = inferredProjectCompilerOptions(projectType, config); const compilerOptions = Object.keys(baseConfig).map(key => `"${key}": ${JSON.stringify(baseConfig[key])}`); return new vscode.SnippetString(`{ "compilerOptions": { @@ -48,13 +67,13 @@ function inferredProjectConfigSnippet( }`); } -export async function openOrCreateConfigFile( - isTypeScriptProject: boolean, +export async function openOrCreateConfig( + projectType: ProjectType, rootPath: string, - config: TypeScriptServiceConfiguration + configuration: TypeScriptServiceConfiguration, ): Promise { - 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); @@ -62,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(config)); + await editor.insertSnippet(inferredProjectConfigSnippet(projectType, configuration)); } return editor; } -} \ No newline at end of file +} + +export async function openProjectConfigOrPromptToCreate( + projectType: ProjectType, + client: ITypeScriptServiceClient, + rootPath: string, + configFileName: string, +): Promise { + 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 { + 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 | 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 37947b38810d5..78333b2da0b02 100644 --- a/extensions/typescript-language-features/src/utils/typeConverters.ts +++ b/extensions/typescript-language-features/src/utils/typeConverters.ts @@ -8,13 +8,19 @@ */ 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'; export namespace Range { export const fromTextSpan = (span: Proto.TextSpan): vscode.Range => fromLocations(span.start, span.end); + export const toTextSpan = (range: vscode.Range): Proto.TextSpan => ({ + start: Position.toLocation(range.start), + end: Position.toLocation(range.end) + }); + export const fromLocations = (start: Proto.Location, end: Proto.Location): vscode.Range => new vscode.Range( Math.max(0, start.line - 1), Math.max(start.offset - 1, 0), @@ -90,3 +96,33 @@ export namespace WorkspaceEdit { return workspaceEdit; } } + +export namespace SymbolKind { + export function fromProtocolScriptElementKind(kind: Proto.ScriptElementKind) { + switch (kind) { + case PConst.Kind.module: return vscode.SymbolKind.Module; + case PConst.Kind.class: return vscode.SymbolKind.Class; + case PConst.Kind.enum: return vscode.SymbolKind.Enum; + case PConst.Kind.enumMember: return vscode.SymbolKind.EnumMember; + 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.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; + case PConst.Kind.variable: return vscode.SymbolKind.Variable; + case PConst.Kind.let: return vscode.SymbolKind.Variable; + case PConst.Kind.const: return vscode.SymbolKind.Variable; + case PConst.Kind.localVariable: return vscode.SymbolKind.Variable; + case PConst.Kind.alias: return vscode.SymbolKind.Variable; + case PConst.Kind.function: return vscode.SymbolKind.Function; + case PConst.Kind.localFunction: return vscode.SymbolKind.Function; + case PConst.Kind.constructSignature: return vscode.SymbolKind.Constructor; + case PConst.Kind.constructorImplementation: return vscode.SymbolKind.Constructor; + case PConst.Kind.typeParameter: return vscode.SymbolKind.TypeParameter; + case PConst.Kind.string: return vscode.SymbolKind.String; + default: return vscode.SymbolKind.Variable; + } + } +} diff --git a/extensions/typescript-language-features/src/utils/typingsStatus.ts b/extensions/typescript-language-features/src/utils/typingsStatus.ts index a070b205621c7..6106fa812eb1a 100644 --- a/extensions/typescript-language-features/src/utils/typingsStatus.ts +++ b/extensions/typescript-language-features/src/utils/typingsStatus.ts @@ -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('check.npmIsInstalled', true)) { - vscode.window.showWarningMessage( + if (config.get('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 20bae2ab64d6c..0000000000000 --- 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(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(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/versionProvider.ts b/extensions/typescript-language-features/src/utils/versionProvider.ts deleted file mode 100644 index 2864d9f927389..0000000000000 --- a/extensions/typescript-language-features/src/utils/versionProvider.ts +++ /dev/null @@ -1,219 +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 * 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'; - -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, - private readonly _pathLabel?: string - ) { } - - public get tsServerPath(): string { - return path.join(this.path, 'tsserver.js'); - } - - public get pathLabel(): string { - return this._pathLabel ?? this.path; - } - - public get isValid(): boolean { - return this.apiVersion !== undefined; - } - - public get apiVersion(): API | undefined { - const version = this.getTypeScriptVersion(this.tsServerPath); - if (version) { - return version; - } - - // Allow TS developers to provide custom version - const tsdkVersion = vscode.workspace.getConfiguration().get('typescript.tsdk_version', undefined); - if (tsdkVersion) { - return API.fromVersionString(tsdkVersion); - } - - 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 { - if (!fs.existsSync(serverPath)) { - return undefined; - } - - const p = serverPath.split(path.sep); - if (p.length <= 2) { - return undefined; - } - const p2 = p.slice(0, -2); - const modulePath = p2.join(path.sep); - let fileName = path.join(modulePath, 'package.json'); - if (!fs.existsSync(fileName)) { - // Special case for ts dev versions - if (path.basename(modulePath) === 'built') { - fileName = path.join(modulePath, '..', 'package.json'); - } - } - if (!fs.existsSync(fileName)) { - return undefined; - } - - const contents = fs.readFileSync(fileName).toString(); - let desc: any = null; - try { - desc = JSON.parse(contents); - } catch (err) { - return undefined; - } - if (!desc || !desc.version) { - return undefined; - } - 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(); - 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/utils/versionStatus.ts b/extensions/typescript-language-features/src/utils/versionStatus.ts deleted file mode 100644 index 9d6ebeda69e71..0000000000000 --- 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/src/utils/wireProtocol.ts b/extensions/typescript-language-features/src/utils/wireProtocol.ts deleted file mode 100644 index 64450f14280ea..0000000000000 --- a/extensions/typescript-language-features/src/utils/wireProtocol.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 * as stream from 'stream'; -import * as vscode from 'vscode'; -import { Disposable } from './dispose'; - -const defaultSize: number = 8192; -const contentLength: string = 'Content-Length: '; -const contentLengthSize: number = Buffer.byteLength(contentLength, 'utf8'); -const blank: number = Buffer.from(' ', 'utf8')[0]; -const backslashR: number = Buffer.from('\r', 'utf8')[0]; -const backslashN: number = Buffer.from('\n', 'utf8')[0]; - -class ProtocolBuffer { - - private index: number = 0; - private buffer: Buffer = Buffer.allocUnsafe(defaultSize); - - public append(data: string | Buffer): void { - let toAppend: Buffer | null = null; - if (Buffer.isBuffer(data)) { - toAppend = data; - } else { - toAppend = Buffer.from(data, 'utf8'); - } - if (this.buffer.length - this.index >= toAppend.length) { - toAppend.copy(this.buffer, this.index, 0, toAppend.length); - } else { - let newSize = (Math.ceil((this.index + toAppend.length) / defaultSize) + 1) * defaultSize; - if (this.index === 0) { - this.buffer = Buffer.allocUnsafe(newSize); - toAppend.copy(this.buffer, 0, 0, toAppend.length); - } else { - this.buffer = Buffer.concat([this.buffer.slice(0, this.index), toAppend], newSize); - } - } - this.index += toAppend.length; - } - - public tryReadContentLength(): number { - let result = -1; - let current = 0; - // we are utf8 encoding... - while (current < this.index && (this.buffer[current] === blank || this.buffer[current] === backslashR || this.buffer[current] === backslashN)) { - current++; - } - if (this.index < current + contentLengthSize) { - return result; - } - current += contentLengthSize; - let start = current; - while (current < this.index && this.buffer[current] !== backslashR) { - current++; - } - if (current + 3 >= this.index || this.buffer[current + 1] !== backslashN || this.buffer[current + 2] !== backslashR || this.buffer[current + 3] !== backslashN) { - return result; - } - let data = this.buffer.toString('utf8', start, current); - result = parseInt(data); - this.buffer = this.buffer.slice(current + 4); - this.index = this.index - (current + 4); - return result; - } - - public tryReadContent(length: number): string | null { - if (this.index < length) { - return null; - } - let result = this.buffer.toString('utf8', 0, length); - let sourceStart = length; - while (sourceStart < this.index && (this.buffer[sourceStart] === backslashR || this.buffer[sourceStart] === backslashN)) { - sourceStart++; - } - this.buffer.copy(this.buffer, 0, sourceStart); - this.index = this.index - sourceStart; - return result; - } -} - -export class Reader extends Disposable { - - private readonly buffer: ProtocolBuffer = new ProtocolBuffer(); - private nextMessageLength: number = -1; - - public constructor(readable: stream.Readable) { - super(); - readable.on('data', data => this.onLengthData(data)); - } - - private readonly _onError = this._register(new vscode.EventEmitter()); - public readonly onError = this._onError.event; - - private readonly _onData = this._register(new vscode.EventEmitter()); - public readonly onData = this._onData.event; - - private onLengthData(data: Buffer | string): void { - if (this.isDisposed) { - return; - } - - try { - this.buffer.append(data); - while (true) { - if (this.nextMessageLength === -1) { - this.nextMessageLength = this.buffer.tryReadContentLength(); - if (this.nextMessageLength === -1) { - return; - } - } - const msg = this.buffer.tryReadContent(this.nextMessageLength); - if (msg === null) { - return; - } - this.nextMessageLength = -1; - const json = JSON.parse(msg); - this._onData.fire(json); - } - } catch (e) { - this._onError.fire(e); - } - } -} 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 0000000000000..4098e94951d34 --- /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 0000000000000..e69de29bb2d1d 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 0000000000000..e69de29bb2d1d 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 0000000000000..e69de29bb2d1d 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 0000000000000..bf3bdd34f1925 --- /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 d83a081792034..7a2eafbfa8461 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 a45e26290c6ec..147be55e1c884 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -2,6 +2,34 @@ # yarn lockfile v1 +"@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" + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -16,20 +44,25 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/json-schema@^7.0.4": + version "7.0.5" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" + integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== + "@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 "12.0.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.2.tgz#3452a24edf9fea138b48fad4a0a028a683da1e40" - integrity sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA== + 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==" "@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== + 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==" "@types/rimraf@2.0.2": version "2.0.2" @@ -44,51 +77,45 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== -ajv@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= +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: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" + es6-promisify "^5.0.0" -ansi-cyan@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" - integrity sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM= +aggregate-error@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" + integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== dependencies: - ansi-wrap "0.1.0" + clean-stack "^2.0.0" + indent-string "^4.0.0" -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= - dependencies: - ansi-wrap "0.1.0" +ajv-keywords@^3.4.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.1.tgz#b83ca89c5d42d69031f424cad49aada0236c6957" + integrity sha512-KWcq3xN8fDjSB+IMoh2VaXVhRI0BBGxoYp3rx7Pkb6z0cFjYR9Q9l4yZqqals0/zsioCmocC5H6UvsGD4MoIBA== -ansi-red@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" - integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= +ajv@^6.12.2: + version "6.12.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" + integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== dependencies: - ansi-wrap "0.1.0" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -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= + 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-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= +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" applicationinsights@1.0.8: version "1.0.8" @@ -99,67 +126,17 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" -arr-diff@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" - integrity sha1-aHwydYFjWI/vfeezb6vklesaOZo= - dependencies: - arr-flatten "^1.0.1" - array-slice "^0.2.3" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= - dependencies: - arr-flatten "^1.0.1" - -arr-flatten@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^2.0.1: +array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" - integrity sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0= - -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= - -array-slice@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" - integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU= - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1, array-uniq@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y= + 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" @@ -176,10 +153,10 @@ aws-sign2@~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.6.0: - version "1.7.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.9.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c" + integrity "sha1-JDkOatYThrCnRyZXVNKhchnehiw= sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" balanced-match@^1.0.0: version "1.0.0" @@ -187,37 +164,16 @@ balanced-match@^1.0.0: integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" - integrity sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40= + 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" -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" - integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" - -boom@4.x.x: - version "4.3.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" - integrity sha1-T4owBctKfjiJ90kDD9JbluAdLjE= - dependencies: - hoek "4.x.x" - -boom@5.x.x: - version "5.2.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" - integrity sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw== - dependencies: - hoek "4.x.x" +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" @@ -227,129 +183,110 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= +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: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" + fill-range "^7.0.1" -browser-stdout@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" - integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= - -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= +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.0.0" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531" - integrity sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA== + 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== + +cacache@^15.0.4: + version "15.0.5" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" + integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== + dependencies: + "@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" caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.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@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" - integrity sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8= - -clone@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -clone@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" - integrity sha1-0hfR6WERjjrJpLi7oyhVU79kfNs= +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== -cloneable-readable@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.2.tgz#d591dee4a8f8bc15da43ce97dceeba13d43e2a65" - integrity sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg== +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: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" + delayed-stream "~1.0.0" -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= +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== -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +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== -combined-stream@1.0.6, combined-stream@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - integrity sha1-cj599ugBrFYTETp+RFqbactjKBg= - dependencies: - delayed-stream "~1.0.0" - -commander@2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" - integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== +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= -convert-source-map@^1.1.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" - integrity sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU= - -core-util-is@1.0.2, core-util-is@~1.0.0: +copy-webpack-plugin@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz#2b3d2bfc6861b96432a65f0149720adbd902040b" + integrity sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA== + dependencies: + cacache "^15.0.4" + 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.1" + schema-utils "^2.7.0" + serialize-javascript "^4.0.0" + webpack-sources "^1.4.3" + +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= -cryptiles@3.x.x: - version "3.1.2" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" - integrity sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4= - dependencies: - boom "5.x.x" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -357,11 +294,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -dateformat@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" - integrity sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI= - debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -369,12 +301,12 @@ debug@3.1.0: dependencies: ms "2.0.0" -deep-assign@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/deep-assign/-/deep-assign-1.0.0.tgz#b092743be8427dc621ea0067cdec7e70dd19f37b" - integrity sha1-sJJ0O+hCfcYh6gBnzex+cN0Z83s= +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: - is-obj "^1.0.0" + ms "^2.1.1" delayed-stream@~1.0.0: version "1.0.0" @@ -393,104 +325,52 @@ diagnostic-channel@0.2.0: dependencies: semver "^5.3.0" -diff@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" - integrity sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww== - -duplexer2@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" - integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= - dependencies: - readable-stream "~1.1.9" - -duplexer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" - integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= +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== -duplexify@^3.2.0: - version "3.5.4" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.4.tgz#4bb46c1796eabebeec4ca9a2e66b808cb7a3d8b4" - integrity sha512-JzYSLYMhoVVBe8+mbHQ4KgpvHpm0DZpJuL8PY93Vyv1fW7jYJ90LoXa1di/CVbJM+TgMs91rbDapE/RNIfnJsA== +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: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" + path-type "^4.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" - integrity sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU= + 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" -end-of-stream@^1.0.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== +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" + 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: - once "^1.4.0" + es6-promise "^4.0.3" -escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2: +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= -event-stream@^3.3.1, event-stream@^3.3.4, event-stream@~3.3.4: - version "3.3.4" - resolved "http://registry.npmjs.org/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" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= - dependencies: - is-posix-bracket "^0.1.0" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= - dependencies: - fill-range "^2.1.0" - -extend-shallow@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" - integrity sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE= - dependencies: - kind-of "^1.1.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend@^3.0.0, extend@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ= - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= - dependencies: - is-extglob "^1.0.0" +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" @@ -502,99 +382,90 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fancy-log@^1.1.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" - integrity sha1-9BEl49hPLn2JpD0G2VjI94vha+E= - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - 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@^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-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.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.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + 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==" -fd-slicer@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" - integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= +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: - pend "~1.2.0" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= + reusify "^1.0.4" -fill-range@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - integrity sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM= +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: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^1.1.3" - repeat-element "^1.1.2" - repeat-string "^1.5.2" + to-regex-range "^5.0.1" -first-chunk-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" - integrity sha1-Wb+1DNkF9g18OUzT2ayqtOatk04= - -for-in@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +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" -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= +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: - for-in "^1.0.1" + locate-path "^5.0.0" + path-exists "^4.0.0" 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.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" - integrity sha1-SXBJi+YEwgwAXU9cI67NIda0kJk= +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" + 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-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= -fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -602,44 +473,14 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= +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: - glob-parent "^2.0.0" - is-glob "^2.0.0" + is-glob "^4.0.1" -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= - dependencies: - is-glob "^2.0.0" - -glob-parent@^3.0.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-stream@^5.3.2: - version "5.3.5" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-5.3.5.tgz#a55665a9a8ccdc41915a87c701e32d4e016fad22" - integrity sha1-pVZlqajM3EGRWofHAeMtTgFvrSI= - dependencies: - extend "^3.0.0" - glob "^5.0.3" - glob-parent "^3.0.0" - micromatch "^2.3.7" - ordered-read-streams "^0.3.0" - through2 "^0.6.0" - to-absolute-glob "^0.1.1" - unique-stream "^2.0.2" - -glob@7.1.2, glob@^7.0.5, glob@^7.1.2: +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== @@ -651,21 +492,10 @@ glob@7.1.2, glob@^7.0.5, glob@^7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^5.0.3: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - 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.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== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -674,187 +504,53 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glogg@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" - integrity sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw== +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: - sparkles "^1.0.0" + 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" -graceful-fs@^4.0.0, graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= - -growl@1.10.3: - version "1.10.3" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.3.tgz#1926ba90cf3edfe2adb4927f5880bc22c66c790f" - integrity sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q== - -gulp-chmod@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/gulp-chmod/-/gulp-chmod-2.0.0.tgz#00c390b928a0799b251accf631aa09e01cc6299c" - integrity sha1-AMOQuSigeZslGsz2MaoJ4BzGKZw= - dependencies: - deep-assign "^1.0.0" - stat-mode "^0.2.0" - through2 "^2.0.0" - -gulp-filter@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/gulp-filter/-/gulp-filter-5.1.0.tgz#a05e11affb07cf7dcf41a7de1cb7b63ac3783e73" - integrity sha1-oF4Rr/sHz33PQafeHLe2OsN4PnM= - dependencies: - multimatch "^2.0.0" - plugin-error "^0.1.2" - streamfilter "^1.0.5" - -gulp-gunzip@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulp-gunzip/-/gulp-gunzip-1.0.0.tgz#15b741145e83a9c6f50886241b57cc5871f151a9" - integrity sha1-FbdBFF6Dqcb1CIYkG1fMWHHxUak= - dependencies: - through2 "~0.6.5" - vinyl "~0.4.6" - -gulp-remote-src-vscode@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/gulp-remote-src-vscode/-/gulp-remote-src-vscode-0.5.0.tgz#71785553bc491880088ad971f90910c4b2d80a99" - integrity sha512-/9vtSk9eI9DEWCqzGieglPqmx0WUQ9pwPHyHFpKmfxqdgqGJC2l0vFMdYs54hLdDsMDEZFLDL2J4ikjc4hQ5HQ== - dependencies: - event-stream "^3.3.4" - node.extend "^1.1.2" - request "^2.79.0" - through2 "^2.0.3" - vinyl "^2.0.1" - -gulp-sourcemaps@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz#b86ff349d801ceb56e1d9e7dc7bbcb4b7dee600c" - integrity sha1-uG/zSdgBzrVuHZ59x7vLS33uYAw= - dependencies: - convert-source-map "^1.1.1" - graceful-fs "^4.1.2" - strip-bom "^2.0.0" - through2 "^2.0.0" - vinyl "^1.0.0" - -gulp-symdest@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/gulp-symdest/-/gulp-symdest-1.1.0.tgz#c165320732d192ce56fd94271ffa123234bf2ae0" - integrity sha1-wWUyBzLRks5W/ZQnH/oSMjS/KuA= - dependencies: - event-stream "^3.3.1" - mkdirp "^0.5.1" - queue "^3.1.0" - vinyl-fs "^2.4.3" - -gulp-untar@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/gulp-untar/-/gulp-untar-0.0.6.tgz#d6bdefde7e9a8e054c9f162385a0782c4be74000" - integrity sha1-1r3v3n6ajgVMnxYjhaB4LEvnQAA= - dependencies: - event-stream "~3.3.4" - gulp-util "~3.0.8" - streamifier "~0.1.1" - tar "^2.2.1" - through2 "~2.0.3" - -gulp-util@~3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" - integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp-vinyl-zip@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/gulp-vinyl-zip/-/gulp-vinyl-zip-2.1.0.tgz#24e40685dc05b7149995245099e0590263be8dad" - integrity sha1-JOQGhdwFtxSZlSRQmeBZAmO+ja0= - dependencies: - event-stream "^3.3.1" - queue "^4.2.1" - through2 "^2.0.3" - vinyl "^2.0.2" - vinyl-fs "^2.0.0" - yauzl "^2.2.1" - yazl "^2.2.1" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= - dependencies: - glogg "^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.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - integrity sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0= +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 "^5.1.0" + ajv "^6.5.5" 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" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= - dependencies: - sparkles "^1.0.0" - -hawk@~6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" - integrity sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ== - dependencies: - boom "4.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - sntp "2.x.x" +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= -hoek@4.x.x: - version "4.2.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" - integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== +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" @@ -865,6 +561,34 @@ http-signature@~1.2.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" + +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" @@ -873,128 +597,33 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, 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= - -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-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= +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-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= - -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-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= - dependencies: - is-extglob "^1.0.0" - -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-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= +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: - kind-of "^3.0.2" - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= + is-extglob "^2.1.1" -is-stream@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +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-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-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-valid-glob@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-0.3.0.tgz#d4b55c69f51886f9b65c70d6c2622d37e29f48fe" - integrity sha1-1LVcafUYhvm2XHDWwmItN+KfSP4= - -is@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" - integrity sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU= - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -1005,37 +634,32 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -json-schema-traverse@^0.3.0: - version "0.3.1" - 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@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - 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.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== +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" -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= +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== jsprim@^1.2.2: version "1.4.1" @@ -1047,180 +671,62 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -kind-of@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" - integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ= - -kind-of@^3.0.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= +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: - is-buffer "^1.1.5" + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= +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: - is-buffer "^1.1.5" + p-locate "^4.1.0" -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= +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: - readable-stream "^2.0.5" + yallist "^4.0.0" -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= - -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" - integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= - -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" - integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" - integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= +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" -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" - integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +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" -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= +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==" -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" - integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= +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==" dependencies: - lodash._root "^3.0.0" + mime-db "1.43.0" -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= - -lodash.isequal@^4.0.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= - -lodash.template@^3.0.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" - integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" - integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - -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= - -merge-stream@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" - integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= - dependencies: - readable-stream "^2.0.1" - -micromatch@^2.3.7: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== - -mime-types@^2.1.12, mime-types@~2.1.17: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== - dependencies: - mime-db "~1.33.0" - -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.4: +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== @@ -1232,319 +738,283 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +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.3" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz#55f7839307d74859d6e8ada9c3ebe72cec216a34" + integrity sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ== + 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.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3" + integrity sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, 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@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.1.0.tgz#7d86cfbcf35cb829e2754c32e17355ec05338794" - integrity sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA== +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" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== dependencies: - browser-stdout "1.3.0" - commander "2.11.0" + browser-stdout "1.3.1" + commander "2.15.1" debug "3.1.0" - diff "3.3.1" + diff "3.5.0" escape-string-regexp "1.0.5" glob "7.1.2" - growl "1.10.3" + growl "1.10.5" he "1.1.1" + minimatch "3.0.4" mkdirp "0.5.1" - supports-color "4.4.0" + 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= -multimatch@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" - integrity sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis= - dependencies: - array-differ "^1.0.0" - array-union "^1.0.1" - arrify "^1.0.0" - minimatch "^3.0.0" - -multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" - integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= - dependencies: - duplexer2 "0.0.2" - -node.extend@^1.1.2: - version "1.1.6" - resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-1.1.6.tgz#a7b882c82d6c93a4863a5504bd5de8ec86258b96" - integrity sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y= - dependencies: - is "^3.1.0" - -normalize-path@^2.0.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" - -oauth-sign@~0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= +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== -object-assign@^3.0.0: +normalize-path@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -object-assign@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +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.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -once@^1.3.0, once@^1.4.0: +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" -ordered-read-streams@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b" - integrity sha1-cTfmmzKYuzQiR6G77jiByA4v14s= +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: - is-stream "^1.0.1" - readable-stream "^2.0.1" + p-try "^2.0.0" -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= +p-limit@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" + p-try "^2.0.0" -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= +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= -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" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= +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== 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= -plugin-error@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" - integrity sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4= +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== + +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: - ansi-cyan "^0.1.1" - ansi-red "^0.1.1" - arr-diff "^1.0.1" - arr-union "^2.0.1" - extend-shallow "^1.1.2" + find-up "^4.0.0" -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= +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= -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== +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==" punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -qs@~6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" - integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== +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== -querystringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.0.0.tgz#fa3ed6e68eb15159457c89b37bc6472833195755" - integrity sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw== +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.1.0: - 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" - -queue@^4.2.1: - version "4.4.2" - resolved "https://registry.yarnpkg.com/queue/-/queue-4.4.2.tgz#5a9733d9a8b8bd1b36e934bc9c55ab89b28e29c7" - integrity sha512-fSMRXbwhMwipcDZ08enW2vl+YDmAmhcNcr43sCJL8DIg+CFOsoRLG23ctxA+fwNk1w55SePSiS7oqQQSgQoVJQ== - dependencies: - inherits "~2.0.0" - -randomatic@^1.1.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" - integrity sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how== - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -"readable-stream@>=1.0.33-1 <1.1.0-0": - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.1.5, 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== - 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@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== - dependencies: - is-equal-shallow "^0.1.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" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= - -repeat-string@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -replace-ext@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" - integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= +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== -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= +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" -request@^2.79.0, request@^2.83.0: - version "2.85.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" - integrity sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg== +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.6.0" + aws4 "^1.8.0" caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" + combined-stream "~1.0.6" + extend "~3.0.2" forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - hawk "~6.0.2" + 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.17" - oauth-sign "~0.8.2" + mime-types "~2.1.19" + oauth-sign "~0.9.0" performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - stringstream "~0.0.5" - tough-cookie "~2.3.3" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" tunnel-agent "^0.6.0" - uuid "^3.1.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= -rimraf@2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== - dependencies: - glob "^7.0.5" +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.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +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" -safe-buffer@^5.0.1, safe-buffer@^5.1.1, 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" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +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.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== + +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== + +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== + +schema-utils@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" semver@5.5.1: version "5.5.1" @@ -1552,192 +1022,116 @@ semver@5.5.1: integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== semver@^5.3.0, semver@^5.4.1: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -sntp@2.x.x: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" - integrity sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg== +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@^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: - hoek "4.x.x" + 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: - version "0.5.5" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.5.tgz#0d4af9e00493e855402e8ec36ebed2d266fceb90" - integrity sha512-mR7/Nd5l1z6g99010shcXJiNEaf3fEtmLhRB/sBcQVJGodcHCULPp2y4Sfa43Kv2zq7T+Izmfp/WHCR6dYkQCA== + 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-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== -sparkles@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" - integrity sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM= - -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" - sshpk@^1.7.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" - integrity sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s= + 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" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: 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" -stat-mode@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.2.2.tgz#e6c80b623123d7d80cf132ce538f346289072502" - integrity sha1-5sgLYjEj19gM8TLOU480YokHJQI= - -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.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= - -streamfilter@^1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/streamfilter/-/streamfilter-1.0.7.tgz#ae3e64522aa5a35c061fd17f67620c7653c643c9" - integrity sha512-Gk6KZM+yNA1JpW0KzlZIhjo3EaBJDkYfXtYSbOwNIQ7Zd6006E6+sCFlW1NDvFG/vnXhKmw6TJJgiEQg/8lXfQ== - dependencies: - readable-stream "^2.0.2" - -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_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -stringstream@~0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" - integrity sha1-TkhM1N5aC7vuGORjB3EKioFiGHg= - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-bom-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" - integrity sha1-5xRDmFd9Uaa+0PoZlPoF9D/ZiO4= - dependencies: - first-chunk-stream "^1.0.0" - strip-bom "^2.0.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -supports-color@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" - integrity sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ== +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: - has-flag "^2.0.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -tar@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" + minipass "^3.1.1" -through2-filter@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-2.0.0.tgz#60bc55a0dacb76085db1f9dae99ab43f83d622ec" - integrity sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw= - dependencies: - through2 "~2.0.0" - xtend "~4.0.0" - -through2@^0.6.0, through2@~0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" - integrity sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg= - dependencies: - readable-stream ">=1.0.33-1 <1.1.0-0" - xtend ">=4.0.0 <4.1.0-0" - -through2@^2.0.0, through2@^2.0.3, through2@~2.0.0, through2@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4= +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: - readable-stream "^2.1.5" - xtend "~4.0.1" + has-flag "^3.0.0" -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" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= - -to-absolute-glob@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f" - integrity sha1-HN+kcqnvUMI57maZm2YsoOs5k38= +tar@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" + integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.0" + mkdirp "^1.0.3" + yallist "^4.0.0" + +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: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +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: - extend-shallow "^2.0.1" + is-number "^7.0.0" -tough-cookie@~2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA== +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: @@ -1752,36 +1146,48 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -unique-stream@^2.0.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.2.1.tgz#5aa003cfbe94c5ff866c4e7d668bb1c4dbadb369" - integrity sha1-WqADz76Uxf+GbE59ZouxxNuts2k= +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#1d85be25043f9b5e36a531941ea345dd5a2ca007" + +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: - json-stable-stringify "^1.0.0" - through2-filter "^2.0.0" + unique-slug "^2.0.0" -url-parse@^1.1.9: - version "1.4.0" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.0.tgz#6bfdaad60098c7fe06f623e42b22de62de0d3d75" - integrity sha512-ERuGxDiQ6Xw/agN4tuoCRbmwRuZP0cJ1lJxJubXr5Q/5cDa78+Dc4wfvtxzhzhkm5VvmW6Mf8EVj9SPGN4l8Lg== +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: - querystringify "^2.0.0" - requires-port "^1.0.0" + imurmurhash "^0.1.4" -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +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" -uuid@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" - integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== +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" -vali-date@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6" - integrity sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY= +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" @@ -1792,75 +1198,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vinyl-fs@^2.0.0, vinyl-fs@^2.4.3: - version "2.4.4" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-2.4.4.tgz#be6ff3270cb55dfd7d3063640de81f25d7532239" - integrity sha1-vm/zJwy1Xf19MGNkDegfJddTIjk= - dependencies: - duplexify "^3.2.0" - glob-stream "^5.3.2" - graceful-fs "^4.0.0" - gulp-sourcemaps "1.6.0" - is-valid-glob "^0.3.0" - lazystream "^1.0.0" - lodash.isequal "^4.0.0" - merge-stream "^1.0.0" - mkdirp "^0.5.0" - object-assign "^4.0.0" - readable-stream "^2.0.4" - strip-bom "^2.0.0" - strip-bom-stream "^1.0.0" - through2 "^2.0.0" - through2-filter "^2.0.0" - vali-date "^1.0.0" - vinyl "^1.0.0" - -vinyl-source-stream@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vinyl-source-stream/-/vinyl-source-stream-1.1.2.tgz#62b53a135610a896e98ca96bee3a87f008a8e780" - integrity sha1-YrU6E1YQqJbpjKlr7jqH8Aio54A= - dependencies: - through2 "^2.0.3" - vinyl "^0.4.3" - -vinyl@^0.4.3, vinyl@~0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" - integrity sha1-LzVsh6VQolVGHza76ypbqL94SEc= - dependencies: - clone "^0.2.0" - clone-stats "^0.0.1" - -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" - integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" - integrity sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ= - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^2.0.1, vinyl@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c" - integrity sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw= - 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" - 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" @@ -1868,55 +1205,49 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -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== + +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.10: - version "1.1.17" - resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.17.tgz#cc2a61731e925301f03f003c009cbf454022cd83" - integrity sha512-yNMyrgEua2qyW7+trNNYhA6PeldRrBcwtLtlazkdtzcmkHMKECM/08bPF8HF2ZFuwHgD+8FQsdqd/DvJYQYjJg== +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" - gulp-chmod "^2.0.0" - gulp-filter "^5.0.1" - gulp-gunzip "1.0.0" - gulp-remote-src-vscode "^0.5.0" - gulp-symdest "^1.1.0" - gulp-untar "^0.0.6" - gulp-vinyl-zip "^2.1.0" - mocha "^4.0.1" - request "^2.83.0" + mocha "^5.2.0" + request "^2.88.0" semver "^5.4.1" source-map-support "^0.5.0" - url-parse "^1.1.9" - vinyl-source-stream "^1.1.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= -"xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= - -yauzl@^2.2.1: - version "2.9.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f" - integrity sha1-qBmB6nCleUYTOIPwKcWCGok1mn8= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.0.1" - -yazl@^2.2.1: - version "2.4.3" - resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071" - integrity sha1-7CblzIfVYBud+EMtvdPNLlFzoHE= - dependencies: - buffer-crc32 "~0.2.3" +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" diff --git a/extensions/vb/language-configuration.json b/extensions/vb/language-configuration.json index d9a6b21014a9b..a31b67bec0fe0 100644 --- a/extensions/vb/language-configuration.json +++ b/extensions/vb/language-configuration.json @@ -12,7 +12,7 @@ ["{", "}"], ["[", "]"], ["(", ")"], - ["\"", "\""] + { "open": "\"", "close": "\"", "notIn": ["string"] } ], "surroundingPairs": [ ["{", "}"], @@ -27,4 +27,4 @@ "end": "^\\s*#End Region\\b" } } -} \ No newline at end of file +} diff --git a/extensions/vb/package.json b/extensions/vb/package.json index f41732e9d6e6f..29e3cceedb6c2 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-api-tests/.vscode/launch.json b/extensions/vscode-api-tests/.vscode/launch.json index 8f8a11a2a5e9f..dfce85c8eea63 100644 --- a/extensions/vscode-api-tests/.vscode/launch.json +++ b/extensions/vscode-api-tests/.vscode/launch.json @@ -6,12 +6,16 @@ "name": "Launch Tests", "type": "extensionHost", "request": "launch", - "runtimeExecutable": "${execPath}", - "args": ["${workspaceFolder}/../../", "${workspaceFolder}/testWorkspace", "--extensionDevelopmentPath=${workspaceFolder}", "--extensionTestsPath=${workspaceFolder}/out" ], - "stopOnEntry": false, - "sourceMaps": true, - "outDir": "out", + "args": [ + "${workspaceFolder}/../../", + "${workspaceFolder}/testWorkspace", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], "preLaunchTask": "npm" } ] -} \ No newline at end of file +} diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 6ed66a3932b4a..e72c27967f847 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -1,79 +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" - ], - "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." - } - } - } - ] - }, - "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 a0aabfb09ec8d..79223b12361f5 100644 --- a/extensions/vscode-api-tests/src/extension.ts +++ b/extensions/vscode-api-tests/src/extension.ts @@ -3,809 +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) { - const memFs = enableFs(context); - enableProblems(context); - enableSearch(context, memFs); - enableTasks(); - - 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('

Hello

'), { 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('Hello _World_'), { create: true, overwrite: true }); - memFs.writeFile(vscode.Uri.parse(`memfs:/sample-folder/file.xml`), textEncoder.encode(''), { 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('&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 `/// -/// - -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;k0) { - features.clear(); - var len = this.seedCounts.length; - for (var i = 0;i0) { - 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(); - } - } - - - 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(); - } - } - } - return board; - } - } -} -`; - } - - 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 === '/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 { - // NOT YET SUPPORTED - //context.subscriptions.push(vscode.workspace.registerFileSearchProvider(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 { - return this.getTasks(); - } - - public resolveTask(_task: vscode.Task): vscode.Task | undefined { - const flavor: string = _task.definition.flavor; - if (flavor) { - const definition: CustomBuildTaskDefinition = _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 => { - // 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(); - onDidWrite: vscode.Event = this.writeEmitter.event; - private closeEmitter = new vscode.EventEmitter(); - onDidClose?: vscode.Event = 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 { - return new Promise((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; - - 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 { - - 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(); - 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); - } - - // --- 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 { - const files = new Set(); - - this._doGetFiles(this.root, files); - - return files; - } - - private _doGetFiles(dir: Directory, files: Set): 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 { - const files = this._getFiles(); - const result: vscode.Uri[] = []; - const pattern = new RegExp(this._convertSimple2RegExpPattern(query.pattern)); - - for (const file of files) { - if (pattern.exec(file.name)) { - result.push(file.uri); - } - } - - return result; - } +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 2b4a5f751ab4a..1b4b1e5adf7b8 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 fba9348435c07..1d62a12c170f1 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) { @@ -105,7 +105,7 @@ suite('commands namespace tests', () => { }); test('api-command: vscode.open', function () { - let uri = Uri.parse(workspace.workspaceFolders![0].uri.toString() + '/image.png'); + let uri = Uri.parse(workspace.workspaceFolders![0].uri.toString() + '/far.js'); let a = commands.executeCommand('vscode.open', uri).then(() => assert.ok(true), () => assert.ok(false)); let b = commands.executeCommand('vscode.open', uri, ViewColumn.Two).then(() => assert.ok(true), () => assert.ok(false)); let c = commands.executeCommand('vscode.open').then(() => assert.ok(false), () => assert.ok(true)); 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 ffd8b53e06c22..0b6f13fb01fcb 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 0000000000000..ec976ff53341c --- /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(resolve => sessionTerminatedRetry = resolve); + await commands.executeCommand('workbench.action.debug.stop'); + await sessionTerminatedPromise; + } + + const firstVariablesRetrieved = new Promise(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(resolve => initializedReceived = resolve); + const configurationDonePromise = new Promise(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(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(resolve => variablesReceived = resolve); + await commands.executeCommand('workbench.action.debug.stepOver'); + await thirdVariablesRetrieved; + assert.equal(stoppedEvents, 3); + + const fourthVariablesRetrieved = new Promise(resolve => variablesReceived = resolve); + await commands.executeCommand('workbench.action.debug.stepInto'); + await fourthVariablesRetrieved; + assert.equal(stoppedEvents, 4); + + const fifthVariablesRetrieved = new Promise(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(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 4b6ee382120df..f83a319aeba22 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'); 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 318b0d07bb3b6..7bc2f75973fea 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 edd765036c25a..ea50f38b1b9fa 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: true, + 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 0a828ad8d736c..8c407fb6c8bce 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 => { + 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 => { + 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 d1237703dac17..55fbe0655a7da 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); - }; + 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(); + 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); - }; + 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(run: () => Promise | T, ms: number): Promise { return new Promise(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 6431bad3635bd..01232a99c8c97 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 > 0); + 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(() => (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(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; - } - try { - equal(e.dimensions.columns, 10); - equal(e.dimensions.rows, 5); - equal(e.terminal, terminal); - } catch (e) { - done(e); + // 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(); } - disposables.push(window.onDidCloseTerminal(() => done())); - terminal.dispose(); })); })); const writeEmitter = new EventEmitter(); @@ -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(() => (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 b2ad43d30b08b..53265b35e99b9 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 e785f1d4afbe1..fd97ea9172891 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -3,17 +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); +} + +const testDocument = workspaceFile('bower.json'); -suite('Webview tests', () => { +suite.skip('vscode API - webview', () => { const disposables: vscode.Disposable[] = []; function _register(disposable: T) { @@ -82,7 +86,7 @@ suite('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*/` @@ -125,7 +129,7 @@ suite('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); @@ -146,7 +150,7 @@ suite('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); @@ -168,7 +172,7 @@ suite('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*/` @@ -189,13 +193,12 @@ suite('Webview tests', () => { } }); vscode.postMessage({ type: 'ready' }); - `); 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); @@ -206,10 +209,10 @@ suite('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); @@ -236,47 +239,67 @@ suite('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*/` `); - 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 = await asWebviewUri('/no-such-image.png'); - const response = sendRecieveMessage(webview, { src: imagePath }); - assert.strictEqual((await response).value, false); + 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 = 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*/` @@ -291,18 +314,38 @@ suite('Webview tests', () => { }); `); - 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*/` + + `); + + 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 })); @@ -332,8 +375,30 @@ suite('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*/` + ${expectedText} + `); + 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 c2ec4f13681f3..9187b34e548c1 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); @@ -146,16 +147,36 @@ 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(); + const [docA, docB] = await Promise.all([ - workspace.openTextDocument(await createRandomFile()), - workspace.openTextDocument(await createRandomFile()), + workspace.openTextDocument(randomFile1), + workspace.openTextDocument(randomFile2) ]); for (let c = 0; c < 4; c++) { let editorA = await window.showTextDocument(docA, ViewColumn.One); - assert(window.activeTextEditor === editorA); + assertActiveEditor(editorA); let editorB = await window.showTextDocument(docB, ViewColumn.Two); - assert(window.activeTextEditor === editorB); + assertActiveEditor(editorB); } }); @@ -380,36 +401,36 @@ 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 first', async function () { + const tracker = createQuickPickTracker(); + const first = tracker.nextItem(); + const pick = window.showQuickPick(['eins', 'zwei', 'drei'], { + onDidSelectItem: tracker.onDidSelectItem + }); + assert.equal(await first, 'eins'); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + assert.equal(await pick, 'eins'); + return tracker.done(); + }); 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)); + const tracker = createQuickPickTracker(); + 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)); + 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) => { @@ -421,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(resolve => setTimeout(() => resolve(), 100)); + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + if (await Promise.race([picks, new Promise(resolve => setTimeout(() => resolve(false), 100))]) === false) { + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + if (await Promise.race([picks, new Promise(resolve => setTimeout(() => resolve(false), 1000))]) === false) { + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + if (await Promise.race([picks, new Promise(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(); @@ -518,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(resolve => setTimeout(() => resolve(false), 100))]); + if (r1 !== false) { + return; + } + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + const r2 = await Promise.race([p, new Promise(resolve => setTimeout(() => resolve(false), 1000))]); + if (r2 !== false) { + return; + } + await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); + const r3 = await Promise.race([p, new Promise(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({ @@ -548,6 +591,23 @@ suite('window namespace tests', () => { assert.equal(await result, undefined); }); + function createQuickPickTracker() { + const resolves: ((value: T) => void)[] = []; + let done: () => void; + const unexpected = new Promise((resolve, reject) => { + done = () => resolve(); + resolves.push(reject); + }); + return { + onDidSelectItem: (item: T) => resolves.pop()!(item), + nextItem: () => new Promise(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 => { 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 070b6818d7282..e192c63c0abef 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 @@ -5,9 +5,9 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile } from '../utils'; +import { createRandomFile, withLogDisabled } from '../utils'; -suite('workspace-event', () => { +suite('vscode API - workspace events', () => { const disposables: vscode.Disposable[] = []; @@ -36,14 +36,58 @@ suite('workspace-event', () => { assert.ok(success); assert.ok(onWillCreate); - assert.equal(onWillCreate?.creating.length, 1); - assert.equal(onWillCreate?.creating[0].toString(), newUri.toString()); + assert.equal(onWillCreate?.files.length, 1); + assert.equal(onWillCreate?.files[0].toString(), newUri.toString()); assert.ok(onDidCreate); - assert.equal(onDidCreate?.created.length, 1); - assert.equal(onDidCreate?.created[0].toString(), newUri.toString()); + assert.equal(onDidCreate?.files.length, 1); + assert.equal(onDidCreate?.files[0].toString(), newUri.toString()); }); + test('onWillCreate/onDidCreate, make changes, edit another file', async function () { + + const base = await createRandomFile(); + const baseDoc = await vscode.workspace.openTextDocument(base); + + const newUri = base.with({ path: base.path + '-foo' }); + + disposables.push(vscode.workspace.onWillCreateFiles(e => { + const ws = new vscode.WorkspaceEdit(); + ws.insert(base, new vscode.Position(0, 0), 'HALLO_NEW'); + e.waitUntil(Promise.resolve(ws)); + })); + + const edit = new vscode.WorkspaceEdit(); + edit.createFile(newUri); + + const success = await vscode.workspace.applyEdit(edit); + assert.ok(success); + + assert.equal(baseDoc.getText(), 'HALLO_NEW'); + }); + + test('onWillCreate/onDidCreate, make changes, edit new file fails', withLogDisabled(async function () { + + const base = await createRandomFile(); + + const newUri = base.with({ path: base.path + '-foo' }); + + disposables.push(vscode.workspace.onWillCreateFiles(e => { + const ws = new vscode.WorkspaceEdit(); + ws.insert(e.files[0], new vscode.Position(0, 0), 'nope'); + e.waitUntil(Promise.resolve(ws)); + })); + + const edit = new vscode.WorkspaceEdit(); + edit.createFile(newUri); + + const success = await vscode.workspace.applyEdit(edit); + assert.ok(success); + + assert.equal((await vscode.workspace.fs.readFile(newUri)).toString(), ''); + assert.equal((await vscode.workspace.openTextDocument(newUri)).getText(), ''); + })); + test('onWillDelete/onDidDelete', async function () { const base = await createRandomFile(); @@ -61,12 +105,72 @@ suite('workspace-event', () => { assert.ok(success); assert.ok(onWilldelete); - assert.equal(onWilldelete?.deleting.length, 1); - assert.equal(onWilldelete?.deleting[0].toString(), base.toString()); + assert.equal(onWilldelete?.files.length, 1); + assert.equal(onWilldelete?.files[0].toString(), base.toString()); assert.ok(onDiddelete); - assert.equal(onDiddelete?.deleted.length, 1); - assert.equal(onDiddelete?.deleted[0].toString(), base.toString()); + assert.equal(onDiddelete?.files.length, 1); + assert.equal(onDiddelete?.files[0].toString(), base.toString()); + }); + + test('onWillDelete/onDidDelete, make changes', async function () { + + const base = await createRandomFile(); + const newUri = base.with({ path: base.path + '-NEW' }); + + disposables.push(vscode.workspace.onWillDeleteFiles(e => { + + const edit = new vscode.WorkspaceEdit(); + edit.createFile(newUri); + edit.insert(newUri, new vscode.Position(0, 0), 'hahah'); + e.waitUntil(Promise.resolve(edit)); + })); + + const edit = new vscode.WorkspaceEdit(); + edit.deleteFile(base); + + const success = await vscode.workspace.applyEdit(edit); + assert.ok(success); + }); + + test('onWillDelete/onDidDelete, make changes, del another file', async function () { + + const base = await createRandomFile(); + const base2 = await createRandomFile(); + disposables.push(vscode.workspace.onWillDeleteFiles(e => { + if (e.files[0].toString() === base.toString()) { + const edit = new vscode.WorkspaceEdit(); + edit.deleteFile(base2); + e.waitUntil(Promise.resolve(edit)); + } + })); + + const edit = new vscode.WorkspaceEdit(); + edit.deleteFile(base); + + const success = await vscode.workspace.applyEdit(edit); + assert.ok(success); + + + }); + + test('onWillDelete/onDidDelete, make changes, double delete', async function () { + + const base = await createRandomFile(); + let cnt = 0; + disposables.push(vscode.workspace.onWillDeleteFiles(e => { + if (++cnt === 0) { + const edit = new vscode.WorkspaceEdit(); + edit.deleteFile(e.files[0]); + e.waitUntil(Promise.resolve(edit)); + } + })); + + const edit = new vscode.WorkspaceEdit(); + edit.deleteFile(base); + + const success = await vscode.workspace.applyEdit(edit); + assert.ok(success); }); test('onWillRename/onDidRename', async function () { @@ -87,19 +191,39 @@ suite('workspace-event', () => { assert.ok(success); assert.ok(onWillRename); - assert.equal(onWillRename?.renaming.length, 1); - assert.equal(onWillRename?.renaming[0].oldUri.toString(), oldUri.toString()); - assert.equal(onWillRename?.renaming[0].newUri.toString(), newUri.toString()); + assert.equal(onWillRename?.files.length, 1); + assert.equal(onWillRename?.files[0].oldUri.toString(), oldUri.toString()); + assert.equal(onWillRename?.files[0].newUri.toString(), newUri.toString()); assert.ok(onDidRename); - assert.equal(onDidRename?.renamed.length, 1); - assert.equal(onDidRename?.renamed[0].oldUri.toString(), oldUri.toString()); - assert.equal(onDidRename?.renamed[0].newUri.toString(), newUri.toString()); + assert.equal(onDidRename?.files.length, 1); + assert.equal(onDidRename?.files[0].oldUri.toString(), oldUri.toString()); + assert.equal(onDidRename?.files[0].newUri.toString(), newUri.toString()); + }); + + test('onWillRename - make changes (saved file)', function () { + return testOnWillRename(false); + }); + + test('onWillRename - make changes (dirty file)', function () { + return testOnWillRename(true); }); - test('onWillRename - make changes', async function () { + async function testOnWillRename(withDirtyFile: boolean): Promise { 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'); @@ -109,7 +233,7 @@ suite('workspace-event', () => { disposables.push(vscode.workspace.onWillRenameFiles(e => { onWillRename = e; const edit = new vscode.WorkspaceEdit(); - edit.insert(e.renaming[0].oldUri, new vscode.Position(0, 0), 'FOO'); + edit.insert(e.files[0].oldUri, new vscode.Position(0, 0), 'FOO'); edit.replace(anotherFile, new vscode.Range(0, 0, 0, 3), 'FARBOO'); e.waitUntil(Promise.resolve(edit)); })); @@ -121,11 +245,17 @@ suite('workspace-event', () => { assert.ok(success); assert.ok(onWillRename); - assert.equal(onWillRename?.renaming.length, 1); - assert.equal(onWillRename?.renaming[0].oldUri.toString(), oldUri.toString()); - assert.equal(onWillRename?.renaming[0].newUri.toString(), newUri.toString()); + assert.equal(onWillRename?.files.length, 1); + 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 06728e206d941..2eb21a4c1f9f5 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; @@ -23,6 +23,8 @@ suite('workspace-fs', () => { assert.equal(typeof stat.mtime, 'number'); assert.equal(typeof stat.ctime, 'number'); + assert.ok(stat.mtime > 0); + assert.ok(stat.ctime > 0); const entries = await vscode.workspace.fs.readDirectory(root); assert.ok(entries.length > 0); @@ -138,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 edb404b86532b..37426834335e3 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,24 +28,105 @@ 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 => { + 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: () => { + const result: Task[] = []; + const kind: CustomTestingTaskDefinition = { + type: taskType, + customProp1: 'testing task one' + }; + const writeEmitter = new EventEmitter(); + const execution = new CustomExecution((): Thenable => { + const pty: Pseudoterminal = { + onDidWrite: writeEmitter.event, + open: () => writeEmitter.fire('testing\r\n'), + close: () => isPseudoterminalClosed = true + }; + return Promise.resolve(pty); + }); + const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution); + result.push(task); + return result; + }, + resolveTask(_task: Task): Task | undefined { + try { + assert.fail('resolveTask should not trigger during the test'); + } catch (e) { + done(e); + } + return undefined; + } + })); + commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`); + }); + + test('sync CustomExecution task should flush all data on close', (done) => { + interface CustomTestingTaskDefinition extends TaskDefinition { + /** + * One of the task properties. This can be used to customize the task in the tasks.json + */ + customProp1: string; + } + const taskType: string = 'customTesting'; + const taskName = 'First custom task'; disposables.push(window.onDidOpenTerminal(term => { disposables.push(window.onDidWriteTerminalData(e => { try { - assert.equal(e.data, 'testing\r\n'); + assert.equal(e.data, 'exiting'); } 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(); - })); + disposables.push(window.onDidCloseTerminal(() => done())); term.dispose(); })); })); @@ -55,21 +138,26 @@ suite('workspace-namespace', () => { customProp1: 'testing task one' }; const writeEmitter = new EventEmitter(); + const closeEmitter = new EventEmitter(); const execution = new CustomExecution((): Thenable => { const pty: Pseudoterminal = { onDidWrite: writeEmitter.event, - open: () => writeEmitter.fire('testing\r\n'), - close: () => isPseudoterminalClosed = true + onDidClose: closeEmitter.event, + open: () => { + writeEmitter.fire('exiting'); + closeEmitter.fire(); + }, + 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; }, resolveTask(_task: Task): Task | undefined { try { - assert.fail('resolveTask should not trigger during the test'); + assert.fail('resolveTask should not trigger during the test'); } catch (e) { done(e); } @@ -78,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(async (resolve) => { + const task = new Task({ type: 'testTask' }, TaskScope.Workspace, 'echo', 'testTask', new ShellExecution('echo', ['hello test'])); + let taskExecution: TaskExecution | undefined; + const executeDoneEvent: EventEmitter = new EventEmitter(); + const taskExecutionShouldBeSet: Promise = new Promise(resolve => { + const disposable = executeDoneEvent.event(() => { + resolve(); + disposable.dispose(); + }); + }); + let count = 2; + const progressMade: EventEmitter = 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(async (resolve, reject) => { + class CustomTerminal implements Pseudoterminal { + private readonly writeEmitter = new EventEmitter(); + public readonly onDidWrite: Event = this.writeEmitter.event; + public async close(): Promise { } + private closeEmitter = new EventEmitter(); + onDidClose: Event = 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 => { + 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 175ef8522d15c..6e1034001f4e5 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 } 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', () => { @@ -75,8 +83,8 @@ suite('workspace-namespace', () => { }); }); - test('openTextDocument, untitled is dirty', function () { - return vscode.workspace.openTextDocument(vscode.Uri.parse('untitled:' + join(vscode.workspace.workspaceFolders![0].uri.toString() || '', './newfile.txt'))).then(doc => { + test('openTextDocument, untitled is dirty', async function () { + return vscode.workspace.openTextDocument(vscode.workspace.workspaceFolders![0].uri.with({ scheme: 'untitled', path: posix.join(vscode.workspace.workspaceFolders![0].uri.path, 'newfile.txt') })).then(doc => { assert.equal(doc.uri.scheme, 'untitled'); assert.ok(doc.isDirty); }); @@ -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); - - disposeAll(disposables); - - return deleteFile(file); - }); - }); - }); - }); + let onDidChangeTextDocument = false; + disposables.push(vscode.workspace.onDidChangeTextDocument(e => { + pendingAsserts.push(() => assertEqualPath(e.document.uri.fsPath, file.fsPath)); + onDidChangeTextDocument = true; + })); + + let onDidSaveTextDocument = false; + disposables.push(vscode.workspace.onDidSaveTextDocument(e => { + pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath)); + onDidSaveTextDocument = true; + })); + + 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 () { @@ -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'); }); @@ -598,7 +637,7 @@ suite('workspace-namespace', () => { assert.equal(doc.isDirty, true); }); - test('applyEdit should fail when editing deleted resource', async () => { + test('applyEdit should fail when editing deleted resource', withLogDisabled(async () => { const resource = await createRandomFile(); const edit = new vscode.WorkspaceEdit(); @@ -607,9 +646,9 @@ suite('workspace-namespace', () => { let success = await vscode.workspace.applyEdit(edit); assert.equal(success, false); - }); + })); - test('applyEdit should fail when renaming deleted resource', async () => { + test('applyEdit should fail when renaming deleted resource', withLogDisabled(async () => { const resource = await createRandomFile(); const edit = new vscode.WorkspaceEdit(); @@ -618,9 +657,9 @@ suite('workspace-namespace', () => { let success = await vscode.workspace.applyEdit(edit); assert.equal(success, false); - }); + })); - test('applyEdit should fail when editing renamed from resource', async () => { + test('applyEdit should fail when editing renamed from resource', withLogDisabled(async () => { const resource = await createRandomFile(); const newResource = vscode.Uri.file(resource.fsPath + '.1'); const edit = new vscode.WorkspaceEdit(); @@ -629,7 +668,7 @@ suite('workspace-namespace', () => { let success = await vscode.workspace.applyEdit(edit); assert.equal(success, false); - }); + })); test('applyEdit "edit A -> rename A to B -> edit B"', async () => { await testEditRenameEdit(oldUri => oldUri.with({ path: oldUri.path + 'NEW' })); @@ -662,7 +701,7 @@ suite('workspace-namespace', () => { return uri.with({ path: posix.join(posix.dirname(uri.path), `_${posix.basename(uri.path)}`) }); } - test('WorkspaceEdit: applying edits before and after rename duplicates resource #42633', async function () { + test('WorkspaceEdit: applying edits before and after rename duplicates resource #42633', withLogDisabled(async function () { let docUri = await createRandomFile(); let newUri = nameWithUnderscore(docUri); @@ -675,9 +714,9 @@ suite('workspace-namespace', () => { assert.ok(await vscode.workspace.applyEdit(we)); let doc = await vscode.workspace.openTextDocument(newUri); assert.equal(doc.getText(), 'BarHelloFoo'); - }); + })); - test('WorkspaceEdit: Problem recreating a renamed resource #42634', async function () { + test('WorkspaceEdit: Problem recreating a renamed resource #42634', withLogDisabled(async function () { let docUri = await createRandomFile(); let newUri = nameWithUnderscore(docUri); @@ -695,9 +734,9 @@ suite('workspace-namespace', () => { assert.equal(newDoc.getText(), 'HelloFoo'); let doc = await vscode.workspace.openTextDocument(docUri); assert.equal(doc.getText(), 'Bar'); - }); + })); - test('WorkspaceEdit api - after saving a deleted file, it still shows up as deleted. #42667', async function () { + test('WorkspaceEdit api - after saving a deleted file, it still shows up as deleted. #42667', withLogDisabled(async function () { let docUri = await createRandomFile(); let we = new vscode.WorkspaceEdit(); we.deleteFile(docUri); @@ -710,7 +749,7 @@ suite('workspace-namespace', () => { } catch (e) { assert.ok(true); } - }); + })); test('WorkspaceEdit: edit and rename parent folder duplicates resource #42641', async function () { @@ -741,7 +780,7 @@ suite('workspace-namespace', () => { assert.equal(doc.getText(), 'Hello'); }); - test('WorkspaceEdit: rename resource followed by edit does not work #42638', async function () { + test('WorkspaceEdit: rename resource followed by edit does not work #42638', withLogDisabled(async function () { let docUri = await createRandomFile(); let newUri = nameWithUnderscore(docUri); @@ -753,9 +792,9 @@ suite('workspace-namespace', () => { let doc = await vscode.workspace.openTextDocument(newUri); assert.equal(doc.getText(), 'Hello'); - }); + })); - test('WorkspaceEdit: create & override', async function () { + test('WorkspaceEdit: create & override', withLogDisabled(async function () { let docUri = await createRandomFile('before'); @@ -768,9 +807,9 @@ suite('workspace-namespace', () => { we.createFile(docUri, { overwrite: true }); assert.ok(await vscode.workspace.applyEdit(we)); assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), ''); - }); + })); - test('WorkspaceEdit: create & ignoreIfExists', async function () { + test('WorkspaceEdit: create & ignoreIfExists', withLogDisabled(async function () { let docUri = await createRandomFile('before'); let we = new vscode.WorkspaceEdit(); @@ -782,9 +821,9 @@ suite('workspace-namespace', () => { we.createFile(docUri, { overwrite: true, ignoreIfExists: true }); assert.ok(await vscode.workspace.applyEdit(we)); assert.equal((await vscode.workspace.openTextDocument(docUri)).getText(), ''); - }); + })); - test('WorkspaceEdit: rename & ignoreIfExists', async function () { + test('WorkspaceEdit: rename & ignoreIfExists', withLogDisabled(async function () { let aUri = await createRandomFile('aaa'); let bUri = await createRandomFile('bbb'); @@ -803,9 +842,9 @@ suite('workspace-namespace', () => { we = new vscode.WorkspaceEdit(); we.renameFile(aUri, bUri, { overwrite: true, ignoreIfExists: true }); assert.ok(await vscode.workspace.applyEdit(we)); - }); + })); - test('WorkspaceEdit: delete & ignoreIfNotExists', async function () { + test('WorkspaceEdit: delete & ignoreIfNotExists', withLogDisabled(async function () { let docUri = await createRandomFile(); let we = new vscode.WorkspaceEdit(); @@ -819,7 +858,7 @@ suite('workspace-namespace', () => { we = new vscode.WorkspaceEdit(); we.deleteFile(docUri, { ignoreIfNotExists: true }); assert.ok(await vscode.workspace.applyEdit(we)); - }); + })); test('WorkspaceEdit: insert & rename multiple', async function () { @@ -916,4 +955,23 @@ suite('workspace-namespace', () => { const expected2 = 'import2;import1;'; assert.equal(document.getText(), expected2); }); + + test('The api workspace.applyEdit failed for some case of mixing resourceChange and textEdit #80688', async function () { + const file1 = await createRandomFile(); + const file2 = await createRandomFile(); + let we = new vscode.WorkspaceEdit(); + we.insert(file1, new vscode.Position(0, 0), 'import1;'); + we.insert(file1, new vscode.Position(0, 0), 'import2;'); + + const file2Name = basename(file2.fsPath); + const file2NewUri = vscode.Uri.parse(file2.toString().replace(file2Name, `new/${file2Name}`)); + we.renameFile(file2, file2NewUri); + + await vscode.workspace.applyEdit(we); + + const document = await vscode.workspace.openTextDocument(file1); + const expected = 'import1;import2;'; + // const expected2 = 'import2;import1;'; + assert.equal(document.getText(), expected); + }); }); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index 969a7cd00510a..475fa6cfbf48e 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 { let fakeFile: vscode.Uri; @@ -46,28 +46,29 @@ export function pathEquals(path1: string, path2: string): boolean { export function closeAllEditors(): Thenable { return vscode.commands.executeCommand('workbench.action.closeAllEditors'); +} +export async function revertAllDirty(): Promise { + 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) { - if (isTestTypeActive()) { - const async = !!testCallback.length; - if (async) { - test(name, (done) => testCallback(done)); - } else { - test(name, () => (<() => void | Thenable>testCallback)()); - } - } +export function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); } -function isTestTypeActive(): boolean { - return !!vscode.extensions.getExtension('vscode-resolver-test'); -} +export function withLogDisabled(runnable: () => Promise): () => Promise { + return async (): Promise => { + const logLevel = await vscode.commands.executeCommand('_extensionTests.getLogLevel'); + await vscode.commands.executeCommand('_extensionTests.setLogLevel', 6 /* critical */); -export function delay(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); + try { + await runnable(); + } finally { + await vscode.commands.executeCommand('_extensionTests.setLogLevel', logLevel); + } + }; } diff --git a/extensions/vscode-api-tests/src/workspace-tests/index.ts b/extensions/vscode-api-tests/src/workspace-tests/index.ts index da6644b2fcd7f..9486d8ed3e50e 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: true, + 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 f018f581c425d..1b4ef88325aaa 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 0000000000000..518e00c672474 --- /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/extensions/vscode-api-tests/testWorkspace/debug.js b/extensions/vscode-api-tests/testWorkspace/debug.js new file mode 100644 index 0000000000000..1ca212281c771 --- /dev/null +++ b/extensions/vscode-api-tests/testWorkspace/debug.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +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 0000000000000..15b462975bee1 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 0000000000000..15b462975bee1 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 6045e073d02e5..c976dbc8d0f2a 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -1,20 +1,59 @@ { - "name": "vscode-colorize-tests", - "description": "Colorize tests for VS Code", - "version": "0.0.1", - "publisher": "vscode", - "license": "MIT", - "private": 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" - }, - "devDependencies": { - "@types/node": "^12.11.7", - "mocha-junit-reporter": "^1.17.0", - "mocha-multi-reporters": "^1.1.7", - "vscode": "1.1.5" - } + "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 0000000000000..393305253e5fc 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 0000000000000..0d34ddedb574b --- /dev/null +++ b/extensions/vscode-colorize-tests/producticons/index.html @@ -0,0 +1,3049 @@ + + + + + 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/extensions/vscode-colorize-tests/producticons/mit_license.txt b/extensions/vscode-colorize-tests/producticons/mit_license.txt new file mode 100644 index 0000000000000..effefee5f0ce8 --- /dev/null +++ b/extensions/vscode-colorize-tests/producticons/mit_license.txt @@ -0,0 +1,21 @@ +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 +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/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 0000000000000..dc076aef5f95e --- /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/colorizer.test.ts b/extensions/vscode-colorize-tests/src/colorizer.test.ts index 82892577bdfa2..28a992573b9cd 100644 --- a/extensions/vscode-colorize-tests/src/colorizer.test.ts +++ b/extensions/vscode-colorize-tests/src/colorizer.test.ts @@ -56,7 +56,7 @@ function hasThemeChange(d: any, p: any) : boolean { } } return false; -}; +} suite('colorization', () => { let extensionsFolder = normalize(join(__dirname, '../../')); diff --git a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts new file mode 100644 index 0000000000000..450e7b4874b16 --- /dev/null +++ b/extensions/vscode-colorize-tests/src/colorizerTestMain.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 vscode from 'vscode'; +import * as jsoncParser from 'jsonc-parser'; + +export function activate(context: vscode.ExtensionContext): any { + + const tokenTypes = ['type', 'struct', 'class', 'interface', 'enum', 'parameterType', 'function', 'variable', 'testToken']; + const tokenModifiers = ['static', 'abstract', 'deprecated', 'declaration', 'documentation', 'member', 'async', 'testModifier']; + + const legend = new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers); + + const outputChannel = vscode.window.createOutputChannel('Semantic Tokens Test'); + + const documentSemanticHighlightProvider: vscode.DocumentSemanticTokensProvider = { + provideDocumentSemanticTokens(document: vscode.TextDocument): vscode.ProviderResult { + const builder = new vscode.SemanticTokensBuilder(); + + 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) { + 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); + + outputChannel.appendLine(`line: ${startLine}, character: ${startCharacter}, length ${length}, ${type} (${tokenType}), ${selectedModifiers} ${tokenModifiers.toString(2)}`); + } + + outputChannel.appendLine('---'); + + const visitor: jsoncParser.JSONVisitor = { + onObjectProperty: (property: string, _offset: number, _length: number, startLine: number, startCharacter: number) => { + addToken(property, startLine, startCharacter, property.length + 2); + }, + onLiteralValue: (value: any, _offset: number, length: number, startLine: number, startCharacter: number) => { + if (typeof value === 'string') { + addToken(value, startLine, startCharacter, length); + } + } + }; + jsoncParser.visit(document.getText(), visitor); + + return builder.build(); + } + }; + + + context.subscriptions.push(vscode.languages.registerDocumentSemanticTokensProvider({ pattern: '**/*semantic-test.json' }, documentSemanticHighlightProvider, legend)); + +} diff --git a/extensions/vscode-colorize-tests/src/index.ts b/extensions/vscode-colorize-tests/src/index.ts index 48a666ea0f762..691ba5c6f0708 100644 --- a/extensions/vscode-colorize-tests/src/index.ts +++ b/extensions/vscode-colorize-tests/src/index.ts @@ -10,7 +10,7 @@ const suite = 'Integration Colorize Tests'; const options: any = { ui: 'tdd', - useColors: true, + useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), timeout: 60000 }; @@ -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/src/typings/ref.d.ts b/extensions/vscode-colorize-tests/src/typings/ref.d.ts index a45a0c6353fe2..a17099ac50c08 100644 --- a/extensions/vscode-colorize-tests/src/typings/ref.d.ts +++ b/extensions/vscode-colorize-tests/src/typings/ref.d.ts @@ -3,5 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// +/// /// -/// + diff --git a/extensions/vscode-colorize-tests/test/semantic-test/.vscode/settings.json b/extensions/vscode-colorize-tests/test/semantic-test/.vscode/settings.json new file mode 100644 index 0000000000000..c91ebc862e9c4 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/semantic-test/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "editor.tokenColorCustomizationsExperimental": { + "class": "#00b0b0", + "interface": "#845faf", + "function": "#ff00ff", + "*.declaration": { + "fontStyle": "underline" + }, + "*.declaration.member": { + "fontStyle": "italic bold", + } + } +} \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/test/semantic-test/semantic-test.json b/extensions/vscode-colorize-tests/test/semantic-test/semantic-test.json new file mode 100644 index 0000000000000..1a2eeaf4408e7 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/semantic-test/semantic-test.json @@ -0,0 +1,9 @@ +[ + "class", "function.member.declaration", + "parameterType.declaration", "type", "parameterType.declaration", "type", + "variable.declaration", "parameterNames", + "function.member.declaration", + "interface.declaration", + "function.member.declaration", "function.notInLegend" + +] diff --git a/extensions/vscode-colorize-tests/yarn.lock b/extensions/vscode-colorize-tests/yarn.lock index 23ac03a3948f6..b8a66d65eff4e 100644 --- a/extensions/vscode-colorize-tests/yarn.lock +++ b/extensions/vscode-colorize-tests/yarn.lock @@ -1042,6 +1042,11 @@ json3@3.3.2: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= +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" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" 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 0000000000000..8978b201c9f06 --- /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 0000000000000..08b6702b80a12 --- /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 0000000000000..cced14b9401e1 --- /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 0000000000000..548094c28e5f5 --- /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 0000000000000..0a83f97fce27f --- /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 0000000000000..d66ab665b8e94 --- /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 0000000000000..a60622b2f28e8 --- /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 0000000000000..5edd53b27cc9c --- /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 0000000000000..bf67b19225d11 --- /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 0000000000000..296ddb38fcb30 --- /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 0000000000000..0d39ebbaa5eec --- /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 0000000000000..73c3753c75c2c --- /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 0000000000000..390a93a3a7fed --- /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 0000000000000..4454b25fb2af7 --- /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 0000000000000..f23538e38a765 --- /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 0000000000000..293c02db743d0 --- /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 0000000000000..7cf7f241bdff3 --- /dev/null +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -0,0 +1,1357 @@ +/*--------------------------------------------------------------------------------------------- + * 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.notebook.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.notebook.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.notebook.onDidChangeVisibleNotebookEditors); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await firstEditorOpen; + + const firstEditorClose = getEventOncePromise(vscode.notebook.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.notebook.onDidChangeVisibleNotebookEditors(() => { + count = vscode.notebook.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.notebook.activeNotebookEditor?.document); + assert.equal(cellChangeEventRet.changes.length, 1); + assert.deepEqual(cellChangeEventRet.changes[0], { + start: 1, + deletedCount: 0, + deletedItems: [], + items: [ + vscode.notebook.activeNotebookEditor!.document.cells[1] + ] + }); + + const secondCell = vscode.notebook.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.notebook.activeNotebookEditor!.document, + changes: [ + { + start: 1, + deletedCount: 1, + deletedItems: [secondCell], + items: [] + }, + { + start: 0, + deletedCount: 0, + deletedItems: [], + items: [vscode.notebook.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.notebook.activeNotebookEditor!.document, + cells: [vscode.notebook.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.notebook.activeNotebookEditor!.document, + cells: [vscode.notebook.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.notebook.activeNotebookEditor!.document, + // cells: vscode.notebook.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.notebook.activeNotebookEditor!.selection; + assert.equal(vscode.notebook.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.notebook.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.notebook.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.notebook.activeNotebookEditor; + assert.equal(firstEditor?.active, true); + assert.equal(firstEditor?.visible, true); + + await splitEditor(); + const secondEditor = vscode.notebook.activeNotebookEditor; + assert.equal(secondEditor?.active, true); + assert.equal(secondEditor?.visible, true); + assert.equal(firstEditor?.active, false); + + assert.equal(vscode.notebook.visibleNotebookEditors.length, 2); + + const untitledEditorChange = getEventOncePromise(vscode.notebook.onDidChangeActiveNotebookEditor); + await vscode.commands.executeCommand('workbench.action.files.newUntitledFile'); + await untitledEditorChange; + assert.equal(firstEditor?.visible, true); + assert.equal(firstEditor?.active, false); + assert.equal(secondEditor?.visible, false); + assert.equal(secondEditor?.active, false); + assert.equal(vscode.notebook.visibleNotebookEditors.length, 1); + + const activeEditorClose = getEventOncePromise(vscode.notebook.onDidChangeActiveNotebookEditor); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + await activeEditorClose; + assert.equal(secondEditor?.active, true); + assert.equal(secondEditor?.visible, true); + assert.equal(vscode.notebook.visibleNotebookEditors.length, 2); + + 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.notebook.onDidChangeActiveNotebookEditor); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + await firstEditorOpen; + + const firstEditorDeactivate = getEventOncePromise(vscode.notebook.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.notebook.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.notebook.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.notebook.activeNotebookEditor!.document.cells[1], true); + + await saveAllFilesAndCloseAll(resource); + }); + + test('edit API (replaceOutput)', async function () { + assertInitalState(); + const resource = await createRandomFile('', undefined, 'first', '.vsctestnb'); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await vscode.notebook.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellOutput(0, [{ outputKind: vscode.CellOutputKind.Rich, data: { foo: 'bar' } }]); + }); + + const document = vscode.notebook.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.notebook.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellOutput(0, [{ outputKind: vscode.CellOutputKind.Rich, data: { foo: 'bar' } }]); + }); + + const value = await outputChangeEvent; + assert.strictEqual(value.document === vscode.notebook.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.notebook.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + }); + + const document = vscode.notebook.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.notebook.activeNotebookEditor!.edit(editBuilder => { + editBuilder.replaceCellMetadata(0, { inputCollapsed: true, executionOrder: 17 }); + }); + + const data = await event; + assert.strictEqual(data.document, vscode.notebook.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.notebook.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.notebook.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.notebook.activeNotebookEditor!.document.version; + await vscode.notebook.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.notebook.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.notebook.activeNotebookEditor!.document.version; + await vscode.notebook.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.notebook.activeNotebookEditor!.document.cells.length, 2); + assert.strictEqual(vscode.notebook.activeNotebookEditor!.document.cells[0]?.metadata?.runnable, false); + assert.strictEqual(version + 1, vscode.notebook.activeNotebookEditor!.document.version); + + await vscode.commands.executeCommand('undo'); + assert.strictEqual(version + 2, vscode.notebook.activeNotebookEditor!.document.version); + assert.strictEqual(vscode.notebook.activeNotebookEditor!.document.cells[0]?.metadata?.runnable, undefined); + assert.strictEqual(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined); + assert.equal(activeCell!.document.getText(), ''); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + + // ---- insert cell below and focus ---- // + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + + // ---- insert cell above and focus ---- // + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + let activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined); + assert.equal(activeCell!.document.getText(), ''); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + + // ---- focus bottom ---- // + await vscode.commands.executeCommand('notebook.focusBottom'); + activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2); + + // ---- focus top and then copy down ---- // + await vscode.commands.executeCommand('notebook.focusTop'); + activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); + + await vscode.commands.executeCommand('notebook.cell.copyDown'); + activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + assert.equal(activeCell?.document.getText(), 'test'); + + await vscode.commands.executeCommand('notebook.cell.delete'); + activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.equal(vscode.notebook.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.notebook.activeNotebookEditor!.document.cells.length, 4); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].document.getText(), 'test'); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].document.getText(), 'test'); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].document.getText(), ''); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[3].document.getText(), ''); + activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); + + + // ---- move up and down ---- // + + await vscode.commands.executeCommand('notebook.cell.moveDown'); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1, + `first move down, active cell ${vscode.notebook.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.notebook.activeNotebookEditor!.selection!.document.getText()}`); + + // await vscode.commands.executeCommand('notebook.cell.moveDown'); + // activeCell = vscode.notebook.activeNotebookEditor!.selection; + + // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 2, + // `second move down, active cell ${vscode.notebook.activeNotebookEditor!.selection!.uri.toString()}, ${vscode.notebook.activeNotebookEditor!.selection!.document.getText()}`); + // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[0].document.getText(), 'test'); + // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[1].document.getText(), ''); + // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells[2].document.getText(), 'test'); + // assert.equal(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.notebook.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.notebook.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.notebook.activeNotebookEditor!.selection; + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 0); + await vscode.commands.executeCommand('notebook.cell.moveDown'); + await vscode.commands.executeCommand('notebook.cell.moveDown'); + + const newActiveCell = vscode.notebook.activeNotebookEditor!.selection; + assert.deepEqual(activeCell, newActiveCell); + + await saveFileAndCloseAll(resource); + // TODO@rebornix, there are still some events order issue. + // assert.equal(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + // const editor = vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + const editor = vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + const editor = vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined); + assert.equal(activeCell!.document.getText(), ''); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true); + assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection); + assert.equal(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'test'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.notEqual(vscode.notebook.activeNotebookEditor!.selection, undefined); + assert.equal(activeCell!.document.getText(), ''); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + + + // modify the second cell, delete it + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.notebook.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.notebook.activeNotebookEditor!.document.cells.length, 2); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.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.notebook.activeNotebookEditor!.document.cells.length, 3); + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1); + assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;'); + + // redo + // await vscode.commands.executeCommand('notebook.redo'); + // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 2); + // assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(vscode.notebook.activeNotebookEditor!.selection!), 1); + // assert.equal(vscode.notebook.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.notebook.activeNotebookEditor?.document); + assert.equal(cellChangeEventRet.changes.length, 1); + assert.deepEqual(cellChangeEventRet.changes[0], { + start: 1, + deletedCount: 0, + deletedItems: [], + items: [ + vscode.notebook.activeNotebookEditor!.document.cells[1] + ] + }); + + const secondCell = vscode.notebook.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.notebook.activeNotebookEditor!.document, + changes: [ + { + start: 1, + deletedCount: 1, + deletedItems: [secondCell], + items: [] + }, + { + start: 0, + deletedCount: 0, + deletedItems: [], + items: [vscode.notebook.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.notebook.activeNotebookEditor!.document, + cells: [vscode.notebook.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.notebook.activeNotebookEditor!.document, + cells: [vscode.notebook.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.notebook.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.notebook.activeNotebookEditor !== undefined, true); + // assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + // assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[0], vscode.notebook.activeNotebookEditor?.selection); + // assert.equal(vscode.notebook.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.notebook.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.notebook.activeNotebookEditor !== undefined, true); + // assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + // assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[0], vscode.notebook.activeNotebookEditor?.selection); + // assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 1); + // assert.equal(vscode.notebook.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.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true); + assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 3); + assert.equal(vscode.notebook.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.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.notebook.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.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + + // switch to the first editor + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true); + assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 3); + assert.equal(vscode.notebook.activeNotebookEditor?.selection?.document.getText(), 'var abc = 0;'); + + // switch to the second editor + await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); + assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true); + assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 2); + assert.equal(vscode.notebook.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.notebook.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.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123); + assert.equal(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.document.metadata.custom!['testMetadata'] as boolean, false); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.metadata.custom!['testCellMetadata'] as number, 123); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + + // TODO see #101462 + // await vscode.commands.executeCommand('notebook.cell.copyDown'); + // const activeCell = vscode.notebook.activeNotebookEditor!.selection; + // assert.equal(vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + // assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), ''); + // assert.equal(vscode.notebook.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.notebook.activeNotebookEditor?.document!; + const [cell] = document.cells; + + await saveAllFilesAndCloseAll(document.uri); + assert.strictEqual(vscode.notebook.activeNotebookEditor, undefined); + + // opening a cell-uri opens a notebook editor + await vscode.commands.executeCommand('vscode.open', cell.uri); + + assert.strictEqual(!!vscode.notebook.activeNotebookEditor, true); + // assert.strictEqual(vscode.notebook.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.notebook.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + + assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.document.getText(), 'var abc = 0;'); + assert.equal(vscode.notebook.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.notebook.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.notebook.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.notebook.activeNotebookEditor!.selection; + assert.equal(activeCell?.document.getText(), 'test'); + + await vscode.commands.executeCommand('notebook.cell.copyDown'); + await vscode.commands.executeCommand('notebook.cell.edit'); + activeCell = vscode.notebook.activeNotebookEditor!.selection; + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.indexOf(activeCell!), 1); + assert.equal(activeCell?.document.getText(), 'test'); + + const edit = new vscode.WorkspaceEdit(); + edit.insert(vscode.notebook.activeNotebookEditor!.selection!.uri, new vscode.Position(0, 0), 'var abc = 0;'); + await vscode.workspace.applyEdit(edit); + + assert.equal(vscode.notebook.activeNotebookEditor!.document.cells.length, 2); + assert.notEqual(vscode.notebook.activeNotebookEditor!.document.cells[0].document.getText(), vscode.notebook.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.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + // const uri = vscode.notebook.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.notebook.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 0000000000000..10ab61306dc24 --- /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 0000000000000..03c8c7435ceee --- /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 0000000000000..a17099ac50c08 --- /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 0000000000000..0a8f20a1942d7 --- /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/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb b/extensions/vscode-notebook-tests/test/customRenderer.vsctestnb new file mode 100644 index 0000000000000..a4a092d83492f --- /dev/null +++ b/extensions/vscode-notebook-tests/test/customRenderer.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/empty.vsctestnb b/extensions/vscode-notebook-tests/test/empty.vsctestnb new file mode 100644 index 0000000000000..a4a092d83492f --- /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 0000000000000..a4a092d83492f --- /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 0000000000000..a4a092d83492f --- /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 0000000000000..296ddb38fcb30 --- /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 0000000000000..465cb67d0aa0d --- /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 a458e14185d60..90e3e0e87eedc 100644 --- a/extensions/vscode-test-resolver/package.json +++ b/extensions/vscode-test-resolver/package.json @@ -8,7 +8,7 @@ "engines": { "vscode": "^1.25.0" }, - "extensionKind": "ui", + "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" diff --git a/extensions/xml/package.json b/extensions/xml/package.json index d76a632b10c29..3e5042df70231 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", @@ -43,6 +43,8 @@ ".publishsettings", ".pubxml", ".pubxml.user", + ".rbxlx", + ".rbxmx", ".rdf", ".rng", ".rss", diff --git a/extensions/xml/xml.language-configuration.json b/extensions/xml/xml.language-configuration.json index 702b6dc6eb724..15664cbb4f5f3 100644 --- a/extensions/xml/xml.language-configuration.json +++ b/extensions/xml/xml.language-configuration.json @@ -12,8 +12,8 @@ { "open": "{", "close": "}"}, { "open": "[", "close": "]"}, { "open": "(", "close": ")" }, - { "open": "'", "close": "'" }, - { "open": "\"", "close": "\"" }, + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string"] }, { "open": "", "notIn": [ "comment", "string" ]}, { "open": "", "notIn": [ "comment", "string" ]} ], diff --git a/extensions/xml/xsl.language-configuration.json b/extensions/xml/xsl.language-configuration.json index 7605fc4a7d166..cf787c79eddc2 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/yaml/package.json b/extensions/yaml/package.json index 5b12e0fbc5bfa..c7dec1b1b83e7 100644 --- a/extensions/yaml/package.json +++ b/extensions/yaml/package.json @@ -40,8 +40,8 @@ "[yaml]": { "editor.insertSpaces": true, "editor.tabSize": 2, - "editor.autoIndent": false + "editor.autoIndent": "advanced" } } } -} \ No newline at end of file +} diff --git a/extensions/yaml/test/colorize-results/issue-4008_yaml.json b/extensions/yaml/test/colorize-results/issue-4008_yaml.json index 10329df780e3b..c16426c590f67 100644 --- a/extensions/yaml/test/colorize-results/issue-4008_yaml.json +++ b/extensions/yaml/test/colorize-results/issue-4008_yaml.json @@ -257,10 +257,10 @@ "t": "source.yaml constant.numeric.integer.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } } -] \ No newline at end of file +] diff --git a/extensions/yaml/test/colorize-results/test_yaml.json b/extensions/yaml/test/colorize-results/test_yaml.json index 6c871b3220829..258202824fe9b 100644 --- a/extensions/yaml/test/colorize-results/test_yaml.json +++ b/extensions/yaml/test/colorize-results/test_yaml.json @@ -246,9 +246,9 @@ "t": "source.yaml constant.numeric.float.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -774,9 +774,9 @@ "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml constant.numeric.integer.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -906,9 +906,9 @@ "t": "source.yaml constant.numeric.integer.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1132,4 +1132,4 @@ "hc_black": "string: #CE9178" } } -] \ No newline at end of file +] diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 6cae6ac12deb4..8ed194dd356c1 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== diff --git a/package.json b/package.json index 3b5d6faf2bfe9..70614a6bf86ed 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.41.0", - "distro": "d9c8e16599fe4b979791417422d0575b417fbe61", + "version": "1.50.0", + "distro": "fd69c0b76c1d86e5ea0c8651f87571091fab2c22", "author": { "name": "Microsoft Corporation" }, @@ -10,12 +10,24 @@ "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", + "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/gulpfile.hygiene.js", "gulp": "gulp --max_old_space_size=8192", "electron": "node build/lib/electron", @@ -25,64 +37,86 @@ "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", - "strict-initialization-watch": "tsc --watch -p src/tsconfig.json --noEmit --strictPropertyInitialization", + "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.2", + "graceful-fs": "4.2.3", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", - "iconv-lite": "0.5.0", - "jschardet": "1.6.0", - "keytar": "^4.11.0", - "native-is-elevated": "0.3.0", - "native-keymap": "2.0.0", - "native-watchdog": "1.2.0", - "node-pty": "^0.10.0-beta2", - "nsfw": "1.2.5", - "onigasm-umd": "^2.2.2", - "semver-umd": "^5.5.3", - "spdlog": "^0.9.0", - "sudo-prompt": "9.0.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.2.0", + "native-watchdog": "1.3.0", + "node-pty": "0.10.0-beta17", + "semver-umd": "^5.5.7", + "spdlog": "^0.11.1", + "sudo-prompt": "9.1.1", + "tas-client": "^0.0.950", "v8-inspect-profiler": "^0.0.20", - "vscode-minimist": "^1.2.1", - "vscode-proxy-agent": "^0.5.1", - "vscode-ripgrep": "^1.5.7", - "vscode-sqlite3": "4.0.8", - "vscode-textmate": "^4.3.0", - "xterm": "4.2.0", - "xterm-addon-search": "0.3.0", - "xterm-addon-web-links": "0.2.1", + "vscode-nsfw": "1.2.8", + "vscode-oniguruma": "1.3.1", + "vscode-proxy-agent": "^0.5.2", + "vscode-ripgrep": "^1.9.0", + "vscode-sqlite3": "4.0.10", + "vscode-textmate": "5.2.0", + "xterm": "4.9.0-beta.32", + "xterm-addon-search": "0.7.0", + "xterm-addon-unicode11": "0.2.0", + "xterm-addon-webgl": "0.9.0-beta.4", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, "devDependencies": { "7zip": "0.0.6", + "@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/iconv-lite": "0.0.1", + "@types/http-proxy-agent": "^2.0.1", "@types/keytar": "^4.4.0", + "@types/minimist": "^1.2.0", "@types/mocha": "2.2.39", - "@types/node": "^10.12.12", - "@types/semver": "^5.5.0", + "@types/node": "^12.11.7", "@types/sinon": "^1.16.36", + "@types/vscode-windows-registry": "^1.0.0", "@types/webpack": "^4.4.10", + "@types/windows-foreground-love": "^0.3.0", + "@types/windows-mutex": "^0.4.0", + "@types/windows-process-tree": "^0.2.0", "@types/winreg": "^1.2.30", + "@types/yauzl": "^2.9.1", + "@types/yazl": "^2.4.2", + "@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", + "deemon": "^1.4.0", + "electron": "9.3.0", + "eslint": "6.8.0", + "eslint-plugin-jsdoc": "^19.1.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", @@ -101,17 +135,16 @@ "gulp-replace": "^0.5.4", "gulp-shell": "^0.6.5", "gulp-tsb": "4.0.5", - "gulp-tslint": "^8.1.3", "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": "^2.0.5", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", "jsdom-no-contextify": "^3.1.0", "lazy.js": "^0.4.2", "merge-options": "^1.0.1", @@ -123,25 +156,28 @@ "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", - "tslint": "^5.16.0", - "typescript": "3.7.2", + "tsec": "googleinterns/tsec", + "typescript": "^4.1.0-dev.20200824", "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", @@ -157,4 +193,4 @@ "windows-mutex": "0.3.0", "windows-process-tree": "0.2.4" } -} +} \ No newline at end of file diff --git a/product.json b/product.json index 759d765533399..4e8daa88fbe0b 100644 --- a/product.json +++ b/product.json @@ -11,8 +11,10 @@ "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", @@ -21,6 +23,118 @@ "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.11", + "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.63", + "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.49.7", + "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.6", + "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.8", + "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 1e16cde724c77..c1a32ce532afa 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 51966abe69dc5..0a7b6f7e0a1d1 100644 --- a/remote/package.json +++ b/remote/package.json @@ -3,31 +3,32 @@ "version": "0.0.0", "dependencies": { "applicationinsights": "1.0.8", - "chokidar": "3.2.3", + "chokidar": "3.4.2", "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": "1.6.0", - "native-watchdog": "1.2.0", - "node-pty": "^0.10.0-beta2", - "nsfw": "1.2.5", - "onigasm-umd": "^2.2.2", - "semver-umd": "^5.5.3", - "spdlog": "^0.9.0", - "vscode-minimist": "^1.2.1", - "vscode-proxy-agent": "^0.5.1", - "vscode-ripgrep": "^1.5.7", - "vscode-textmate": "^4.3.0", - "xterm": "4.2.0", - "xterm-addon-search": "0.3.0", - "xterm-addon-web-links": "0.2.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-beta17", + "semver-umd": "^5.5.7", + "spdlog": "^0.11.1", + "vscode-nsfw": "1.2.8", + "vscode-oniguruma": "1.3.1", + "vscode-proxy-agent": "^0.5.2", + "vscode-ripgrep": "^1.9.0", + "vscode-textmate": "5.2.0", + "xterm": "4.9.0-beta.32", + "xterm-addon-search": "0.7.0", + "xterm-addon-unicode11": "0.2.0", + "xterm-addon-webgl": "0.9.0-beta.4", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, "optionalDependencies": { - "vscode-windows-ca-certs": "0.1.0", + "vscode-windows-ca-certs": "0.2.0", "vscode-windows-registry": "1.0.2" } } diff --git a/remote/web/package.json b/remote/web/package.json index 46207aac12481..9db946c5bbb67 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -2,11 +2,14 @@ "name": "vscode-web", "version": "0.0.0", "dependencies": { - "onigasm-umd": "^2.2.2", - "semver-umd": "^5.5.3", - "vscode-textmate": "^4.3.0", - "xterm": "4.2.0", - "xterm-addon-search": "0.3.0", - "xterm-addon-web-links": "0.2.1" + "iconv-lite-umd": "0.6.8", + "jschardet": "2.2.1", + "semver-umd": "^5.5.7", + "vscode-oniguruma": "1.3.1", + "vscode-textmate": "5.2.0", + "xterm": "4.9.0-beta.32", + "xterm-addon-search": "0.7.0", + "xterm-addon-unicode11": "0.2.0", + "xterm-addon-webgl": "0.9.0-beta.4" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 0a1c1d9859aad..400d4d629c45f 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -2,46 +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== - -onigasm-umd@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257" - integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw== - -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" - -semver-umd@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" - integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== - -vscode-textmate@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.3.0.tgz#6e1f0f273d84148cfa1e9c7ed85bd16c974f9f61" - integrity sha512-MhEZ3hvxOVuYGsrRzW/PZLDR2VdtG2+V6TIKPvmE9JT+RAq/OtPlrFd1+ZQwBefoHEhjRNuRJ0OktcFezuxPmg== - dependencies: - oniguruma "^7.2.0" - -xterm-addon-search@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0.tgz#fc6843bebab1873eff0c89e31f7b74bb3c4d8947" - integrity sha512-ZvRmkNBSz2dT00w0qJ3Hd5cwYoOfkVZpHou7Xxy1kO34wQglU/8Ec4DNTvU27sS9BUzEg8kUcTTru0DxhJXrTA== - -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@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0.tgz#b9980b2ae18c64ed31bcc95eace3bcecffebbbd9" - integrity sha512-oNfLqt+LmghLPOt5UcskP5Km1fXpTBHsTZ99nxJKY2N0MNFXBKIVXUcNNhHCa74JGZFMGhLT58A/UN3HcPXSfg== +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== + +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== + +semver-umd@^5.5.7: + version "5.5.7" + resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.7.tgz#966beb5e96c7da6fbf09c3da14c2872d6836c528" + integrity sha512-XgjPNlD0J6aIc8xoTN6GQGwWc2Xg0kq8NzrqMVuKG/4Arl6ab1F8+Am5Y/XKKCR+FceFr2yN/Uv5ZJBhRyRqKg== + +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@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.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0.tgz#c929d3e5cbb335e82bff72f158ea82936d9cd4ef" + integrity sha512-6060evmJJ+tZcjnx33FXaeEHLpuXEa7l9UzUsYfMlCKbu88AbE+5LJocTKCHYd71cwCwb9pjmv/G1o9Rf9Zbcg== + +xterm-addon-unicode11@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c" + integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q== + +xterm-addon-webgl@0.9.0-beta.4: + version "0.9.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0-beta.4.tgz#5f5fde50db5c06b116471bcf56ad9930884b36fa" + integrity sha512-GuCvF7Eg1nKLX6zUbJLkt5cqeeccUjf/G6fugCfrkR0EWWC6Ik5mEsEOs5UWm9vvY2kX9t16BdCrgqp8KJegEg== + +xterm@4.9.0-beta.32: + version "4.9.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.32.tgz#d1243d3be211cc06aad3418e696e4eced995c20c" + integrity sha512-jloHNBnj6XRJt+oPkapvrXJZVsYq6se/PEgzErl0iZn9qzSB3jmWE4byumoEjXJR6EgU5ZOmNljeeEDA9jO/jA== diff --git a/remote/yarn.lock b/remote/yarn.lock index c9f3ddd6ebc48..c5f19e6bbc928 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.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" @@ -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.4.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,15 +167,10 @@ glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -graceful-fs@4.1.11, graceful-fs@^4.1.2: - 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.6: - version "4.2.0" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" - integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== +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== http-proxy-agent@^2.1.0: version "2.1.0" @@ -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@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.6.0.tgz#c7d1a71edcff2839db2f9ec30fc5d5ebd3c1a678" - integrity sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ== +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,25 +274,30 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -nan@^2.0.0, nan@^2.14.0: +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" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== -native-watchdog@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.2.0.tgz#9c710093ac6e9e60b19517cb1ef4ac9d7c997395" - integrity sha512-jOOoA3PLSxt1adaeuEW7ymV9cApZiDxn4y4iFs7b4sP73EG+5Lsz+OgUNFcGMyrtznTw6ZvlLcilIN4jeAIgaQ== +native-watchdog@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.3.0.tgz#88cee94c9dc766b85c8506eda14c8bd8c9618e27" + integrity sha512-WOjGRNGkYZ5MXsntcvCYrKtSYMaewlbCFplbcUVo9bE80LPVt8TAVFHYWB8+a6fWCGYheq21+Wtt6CJrUaCJhw== node-addon-api@1.6.2: version "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,28 +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== -nsfw@1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/nsfw/-/nsfw-1.2.5.tgz#febe581af616f7b042f89df133abe62416c4c803" - integrity sha512-m3mwZUKXiCR69PDMLfAmKmiNzy0Oe9LhFE0DYZC5cc1htNj5Hyb1sAgglXhuaDkibFy22AVvPC5cCFB3A6mYIw== - dependencies: - fs-extra "^7.0.0" - lodash.isinteger "^4.0.4" - lodash.isundefined "^3.0.1" - nan "^2.0.0" - -onigasm-umd@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257" - integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw== - -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" @@ -315,22 +316,27 @@ 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== - dependencies: - picomatch "^2.0.4" +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== -"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== +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== -semver-umd@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" - integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== +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" + +semver-umd@^5.5.7: + version "5.5.7" + resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.7.tgz#966beb5e96c7da6fbf09c3da14c2872d6836c528" + integrity sha512-XgjPNlD0J6aIc8xoTN6GQGwWc2Xg0kq8NzrqMVuKG/4Arl6ab1F8+Am5Y/XKKCR+FceFr2yN/Uv5ZJBhRyRqKg== semver@^5.3.0: version "5.6.0" @@ -358,10 +364,10 @@ socks@~2.3.2: ip "^1.1.5" smart-buffer "4.0.2" -spdlog@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.9.0.tgz#c85dd9d0b9cd385f6f3f5b92dc9d2e1691092b5c" - integrity sha512-AeLWPCYjGi4w5DfpXFKb9pCdgMe4gFBMroGfgwXiNfzwmcNYGoFQkIuD1MChZBR1Iwrx0nGhsTSHFslt/qfTAQ== +spdlog@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.11.1.tgz#29721b31018a5fe6a3ce2531f9d8d43e0bd6b825" + integrity sha512-M+sg9/Tnr0lrfnW2/hqgpoc4Z8Jzq7W8NUn35iiSslj+1uj1pgutI60MCpulDP2QyFzOpC8VsJmYD6Fub7wHoA== dependencies: bindings "^1.5.0" mkdirp "^0.5.1" @@ -379,37 +385,48 @@ 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.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/vscode-minimist/-/vscode-minimist-1.2.1.tgz#e63d3f4a9bf3680dcb8f9304eed612323fd6926a" - integrity sha512-cmB72+qDoiCFJ1UKnGUBdGYfXzdpJ3bQM/D/+XhkVk5v7uZgLbYiCz5JcwVyk7NC7hSi5VGtQ4wihzmi12NeXw== +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== + 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.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.5.1.tgz#7fd15e157c02176a0dca9f87840ad0991a62ca57" - integrity sha512-Nnkc7gBk9iAbbZURYZm3p/wvDvRVwDvdzEvDqF1Jh40p6przwQU/JTlV1XLrmd4cCwh6TOAWD81Z3cq+K7Xdmw== +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" + integrity sha512-1cCNPxrWIrmUwS+1XGaXxkh3G1y7z2fpXl1sT74OZvELaryQWYb3NMxMLJJ4Q/CpPLEyuhp/bAN7nzHxxFcQ5Q== dependencies: debug "^3.1.0" http-proxy-agent "^2.1.0" 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.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.3.0.tgz#6e1f0f273d84148cfa1e9c7ed85bd16c974f9f61" - integrity sha512-MhEZ3hvxOVuYGsrRzW/PZLDR2VdtG2+V6TIKPvmE9JT+RAq/OtPlrFd1+ZQwBefoHEhjRNuRJ0OktcFezuxPmg== +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.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-windows-ca-certs/-/vscode-windows-ca-certs-0.1.0.tgz#d58eeb40b536130918cfde2b01e6dc7e5c1bd757" - integrity sha512-ZfZbfJIE09Q0dwGqmqTj7kuAq4g6lul9WPJvo0DkKjln8/FL+dY3wUKIKbYwWQp4x56SBTLBq3tJkD72xQ9Gqw== +vscode-windows-ca-certs@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/vscode-windows-ca-certs/-/vscode-windows-ca-certs-0.2.0.tgz#086f0f4de57e2760a35ac6920831bff246237115" + integrity sha512-YBrJRT0zos+Yb1Qdn73GD8QZr7pa2IE96b5Y1hmmp6XeR8aYB7Iiq5gDAF/+/AxL+caSR9KPZQ6jiYWh5biD7w== dependencies: node-addon-api "1.6.2" @@ -418,20 +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.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.3.0.tgz#fc6843bebab1873eff0c89e31f7b74bb3c4d8947" - integrity sha512-ZvRmkNBSz2dT00w0qJ3Hd5cwYoOfkVZpHou7Xxy1kO34wQglU/8Ec4DNTvU27sS9BUzEg8kUcTTru0DxhJXrTA== - -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-search@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.7.0.tgz#c929d3e5cbb335e82bff72f158ea82936d9cd4ef" + integrity sha512-6060evmJJ+tZcjnx33FXaeEHLpuXEa7l9UzUsYfMlCKbu88AbE+5LJocTKCHYd71cwCwb9pjmv/G1o9Rf9Zbcg== -xterm@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.2.0.tgz#b9980b2ae18c64ed31bcc95eace3bcecffebbbd9" - integrity sha512-oNfLqt+LmghLPOt5UcskP5Km1fXpTBHsTZ99nxJKY2N0MNFXBKIVXUcNNhHCa74JGZFMGhLT58A/UN3HcPXSfg== +xterm-addon-unicode11@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.2.0.tgz#9ed0c482b353908bba27778893ca80823382737c" + integrity sha512-rjFDItPc/IDoSiEnoDFwKroNwLD/7t9vYKENjrcKVZg5tgJuuUj8D4rZtP6iVCjSB1LTLYmUs4L/EmCqIyLR/Q== + +xterm-addon-webgl@0.9.0-beta.4: + version "0.9.0-beta.4" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.9.0-beta.4.tgz#5f5fde50db5c06b116471bcf56ad9930884b36fa" + integrity sha512-GuCvF7Eg1nKLX6zUbJLkt5cqeeccUjf/G6fugCfrkR0EWWC6Ik5mEsEOs5UWm9vvY2kX9t16BdCrgqp8KJegEg== + +xterm@4.9.0-beta.32: + version "4.9.0-beta.32" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.9.0-beta.32.tgz#d1243d3be211cc06aad3418e696e4eced995c20c" + integrity sha512-jloHNBnj6XRJt+oPkapvrXJZVsYq6se/PEgzErl0iZn9qzSB3jmWE4byumoEjXJR6EgU5ZOmNljeeEDA9jO/jA== yauzl@^2.9.2: version "2.10.0" diff --git a/resources/linux/bin/code.sh b/resources/linux/bin/code.sh index 516c05e4ee086..06973937f1493 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 0000000000000..5a3e286ef431e --- /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 b975e1094a2cb..62d6bfc47b469 --- 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 27d36308c88ac..903640cd5035d 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 Priority: optional Architecture: @@ARCHITECTURE@@ Maintainer: Microsoft Corporation diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template index 2c4a1fdd1d6fa..9f26b35099929 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 c43a2b16ae373..238a566c48608 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 93e20cd4825e4..3407d4a0c51e8 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 34f127e1ae593..07e2b307fcdb7 100644 --- a/resources/linux/rpm/dependencies.json +++ b/resources/linux/rpm/dependencies.json @@ -63,5 +63,135 @@ "libxcb.so.1()(64bit)", "libxkbfile.so.1()(64bit)", "libsecret-1.so.0()(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 2a1c4395187b2..9f4eb6a23b8bc 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 c39e5f4f84951..7dbc1680ba2e4 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -24,6 +24,7 @@ parts: plugin: dump source: . stage-packages: + - ibus-gtk3 - fcitx-frontend-gtk3 - gvfs-libs - libasound2 diff --git a/resources/web/callback.html b/resources/web/callback.html new file mode 100644 index 0000000000000..81f217e980b77 --- /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 0000000000000..e0742e5f83867 --- /dev/null +++ b/resources/web/code-web.js @@ -0,0 +1,608 @@ +#!/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.9'; + +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' + + ' --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 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(); + +const server = http.createServer((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.'); + } +}); + +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 + */ +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); + if (!filePath) { + return serveError(req, res, 400, `Bad request.`); + } + return serveFile(req, res, filePath, { + 'Access-Control-Allow-Origin': '*' + }); + } + + // 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); + if (!filePath) { + return serveError(req, res, 400, `Bad request.`); + } + return serveFile(req, res, filePath, { + 'Access-Control-Allow-Origin': '*' + }); +} + +/** + * @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'], + }; + if (args['wrap-iframe']) { + webConfigJSON._wrapWebWorkerExtHostInIframe = true; + } + + const credentials = []; + if (args['github-auth']) { + const sessionId = uuid.v4(); + credentials.push({ + service: 'code-oss.login', + account: 'account', + password: JSON.stringify({ + id: sessionId, + providerId: 'github', + accessToken: args['github-auth'] + }) + }, { + service: 'code-oss-github.login', + account: 'account', + password: JSON.stringify([{ + id: sessionId, + scopes: ['user:email'], + accessToken: args['github-auth'] + }]) + }); + } + + 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_CREDENTIALS}}', () => escapeAttribute(JSON.stringify(credentials))) + .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) { + 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 ".." + + 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/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 66e11adc2f05c..d86b6e0574af0 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 + WSL_EXT_WLOC=$(cat /tmp/remote-wsl-loc.txt) + + 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" "$@" exit $? fi + elif [ -x "$(command -v cygpath)" ]; then CLI=$(cygpath -m "$VSCODE_PATH/resources/app/out/cli.js") else diff --git a/scripts/code-cli.bat b/scripts/code-cli.bat index 56c3756720114..2e4e34ff32e7d 100644 --- a/scripts/code-cli.bat +++ b/scripts/code-cli.bat @@ -5,32 +5,21 @@ 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%" -:: Download Electron if needed -node build\lib\electron.js -if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js 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 ELECTRON_RUN_AS_NODE=1 set NODE_ENV=development set VSCODE_DEV=1 -set ELECTRON_DEFAULT_ERROR_MODE=1 set ELECTRON_ENABLE_LOGGING=1 set ELECTRON_ENABLE_STACK_DUMPING=1 diff --git a/scripts/code-cli.sh b/scripts/code-cli.sh index e4fa552e64259..a792c08532ee6 100755 --- a/scripts/code-cli.sh +++ b/scripts/code-cli.sh @@ -18,11 +18,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 @@ -30,12 +29,6 @@ function code() { return fi - # Sync built-in extensions - node build/lib/builtInExtensions.js - - # Build - test -d out || yarn compile - ELECTRON_RUN_AS_NODE=1 \ NODE_ENV=development \ VSCODE_DEV=1 \ diff --git a/scripts/code-web.js b/scripts/code-web.js deleted file mode 100755 index 27d538dc1c389..0000000000000 --- a/scripts/code-web.js +++ /dev/null @@ -1,225 +0,0 @@ -#!/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('vscode-minimist'); - -const APP_ROOT = path.dirname(__dirname); -const WEB_MAIN = path.join(APP_ROOT, 'src', 'vs', 'code', 'browser', 'workbench', 'workbench-dev.html'); -const PORT = 8080; - -const args = minimist(process.argv, { - string: [ - 'no-launch' - ] -}); - -const server = http.createServer((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 (/^\/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(PORT, () => { - console.log(`Web UI available at http://localhost:${PORT}`); -}); - -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(APP_ROOT, 'extensions', 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)(path.join(APP_ROOT, 'extensions')); - const mapExtensionFolderToExtensionPackageJSON = new Map(); - - await Promise.all(extensionFolders.map(async extensionFolder => { - try { - const packageJSON = JSON.parse((await util.promisify(fs.readFile)(path.join(APP_ROOT, 'extensions', 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: 'http', authority: `localhost:${PORT}`, 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 { - 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(`http://localhost:${PORT}`); -} diff --git a/scripts/code.bat b/scripts/code.bat index 0eb0eb0b342b8..7ef1fd33fe000 100644 --- a/scripts/code.bat +++ b/scripts/code.bat @@ -5,32 +5,21 @@ 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%" -:: Download Electron if needed -node build\lib\electron.js -if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js 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= diff --git a/scripts/code.sh b/scripts/code.sh index 4ba1a00b9f7c6..b19cc0df9ff43 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,12 +35,6 @@ 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 @@ -50,11 +44,14 @@ function code() { export VSCODE_LOGS= # Launch Code - exec "$CODE" . "$@" + exec "$CODE" . --no-sandbox "$@" } function code-wsl() { + HOST_IP=$(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 diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index 0d7b9ee4b37c9..5a493b0b1c59f 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -3,53 +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 - - echo "Running integration tests with '%INTEGRATION_TEST_ELECTRON_PATH%' as build." + :: 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 + + 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 --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% +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% -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% . +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% -:: 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\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% -if exist ".\resources\server\test\test-remote-integration.bat" ( - call .\resources\server\test\test-remote-integration.bat -) +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 21652b632cf32..6856ebd525ffb 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -4,13 +4,13 @@ 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,17 +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 @@ -37,23 +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 - - -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 +"$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 -# 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 0d1350a79ece2..089973f9e38b9 100644 --- a/scripts/test.bat +++ b/scripts/test.bat @@ -17,7 +17,7 @@ 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 diff --git a/scripts/test.sh b/scripts/test.sh index 2829899329568..651cffb88f504 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -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/.eslintrc b/src/.eslintrc deleted file mode 100644 index c25b0d558c2c3..0000000000000 --- a/src/.eslintrc +++ /dev/null @@ -1,19 +0,0 @@ -{ - "parserOptions": { - "ecmaVersion": 6 - }, - "env": { - "node": true, - "es6": true, - "browser": true, - "amd": true - }, - "rules": { - "no-console": 0, - "no-cond-assign": 0, - "no-unused-vars": "error", - "no-extra-semi": "error", - "semi": "error", - "no-inner-declarations": 0 - } -} \ No newline at end of file diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index a8144075dd59c..b0b266557a62b 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), catchError: true, nodeRequire: require, nodeMain: __filename, diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index e0c6cf34c56c4..97a4d9934cd2f 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; }); - } catch (error) { - 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'; - } + if (res.length > MAX_LENGTH) { + return 'Output omitted for a large object that exceeds the limits'; + } - return res; + return res; + } catch (error) { + return `Output omitted for an object that cannot be inspected ('${error.toString()}')`; + } } + /** + * @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 0000000000000..2686249978d66 --- /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 0336f9eb70034..9d1ddc5a957ef 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -3,216 +3,258 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/// + //@ts-check 'use strict'; -const bootstrap = require('./bootstrap'); - -/** - * @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); -}; - -/** - * - * @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) { - - // @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'] || '{}') || {}; - - // Apply zoom level early to avoid glitches - const zoomLevel = configuration.zoomLevel; - if (typeof zoomLevel === 'number' && zoomLevel !== 0) { - webFrame.setZoomLevel(zoomLevel); +// Simple module style to support node.js and browser environments +(function (globalThis, factory) { + + // Node.js + if (typeof exports === 'object') { + module.exports = factory(); } - // Error handler - // @ts-ignore - process.on('uncaughtException', function (error) { - onUnexpectedError(error, enableDeveloperTools); - }); - - // Developer tools - const enableDeveloperTools = (process.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; - let developerToolsUnbind; - if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { - developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); + // Browser + else { + globalThis.MonacoBootstrapWindow = factory(); } +}(this, function () { + const preloadGlobals = globals(); + const sandbox = preloadGlobals.context.sandbox; + const webFrame = preloadGlobals.webFrame; + const safeProcess = sandbox ? preloadGlobals.process : process; - // Correctly inherit the parent's environment - exports.assign(process.env, configuration.userEnv); + /** + * @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); + } - // Enable ASAR support - bootstrap.enableASARSupport(path.join(configuration.appRoot, 'node_modules')); + // Error handler + safeProcess.on('uncaughtException', function (error) { + onUnexpectedError(error, enableDeveloperTools); + }); - if (options && typeof options.canModifyDOM === 'function') { - options.canModifyDOM(configuration); - } + // Developer tools + const enableDeveloperTools = (safeProcess.env['VSCODE_DEV'] || !!configuration.extensionDevelopmentPath) && !configuration.extensionTestsPath; + let developerToolsUnbind; + if (enableDeveloperTools || (options && options.forceEnableDeveloperKeybindings)) { + developerToolsUnbind = registerDeveloperKeybindings(options && options.disallowReloadKeybinding); + } - // Get the nls configuration into the process.env as early as possible. - const nlsConfig = bootstrap.setupNLS(); + // Correctly inherit the parent's environment (TODO@sandbox non-sandboxed only) + if (!sandbox) { + Object.assign(safeProcess.env, configuration.userEnv); + } - let locale = nlsConfig.availableLanguages['*'] || 'en'; - if (locale === 'zh-tw') { - locale = 'zh-Hant'; - } else if (locale === 'zh-cn') { - locale = 'zh-Hans'; - } + // Enable ASAR support (TODO@sandbox non-sandboxed only) + if (!sandbox) { + globalThis.MonacoBootstrap.enableASARSupport(configuration.appRoot); + } - window.document.documentElement.setAttribute('lang', locale); + if (options && typeof options.canModifyDOM === 'function') { + options.canModifyDOM(configuration); + } - // 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; + // Get the nls configuration into the process.env as early as possible (TODO@sandbox non-sandboxed only) + const nlsConfig = sandbox ? { availableLanguages: {} } : globalThis.MonacoBootstrap.setupNLS(); - window['nodeRequire'] = nodeRequire; - window['require'] = amdRequire; + let locale = nlsConfig.availableLanguages['*'] || 'en'; + if (locale === 'zh-tw') { + locale = 'zh-Hant'; + } else if (locale === 'zh-cn') { + locale = 'zh-Hans'; + } - // replace the patched electron fs with the original node fs for all AMD code - amdDefine('fs', ['original-fs'], function (originalFS) { return originalFS; }); + window.document.documentElement.setAttribute('lang', locale); - window['MonacoEnvironment'] = {}; + // do not advertise AMD to avoid confusing UMD modules loaded with nodejs (TODO@sandbox non-sandboxed only) + if (!sandbox) { + window['define'] = undefined; + } - const loaderConfig = { - baseUrl: bootstrap.uriFromPath(configuration.appRoot) + '/out', - 'vs/nls': nlsConfig, - nodeModules: [/*BUILD->INSERT_NODE_MODULES*/] - }; + // 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'] = {}; - // cached data config - if (configuration.nodeCachedDataDir) { - loaderConfig.nodeCachedData = { - path: configuration.nodeCachedDataDir, - seed: modulePaths.join('') + const loaderConfig = { + baseUrl: `${uriFromPath(configuration.appRoot)}/out`, + 'vs/nls': nlsConfig, + amdModulesPattern: /^vs\//, }; - } - if (options && typeof options.beforeLoaderConfig === 'function') { - options.beforeLoaderConfig(configuration, loaderConfig); - } + // cached data config + if (configuration.nodeCachedDataDir) { + loaderConfig.nodeCachedData = { + path: configuration.nodeCachedDataDir, + seed: modulePaths.join('') + }; + } - amdRequire.config(loaderConfig); + if (options && typeof options.beforeLoaderConfig === 'function') { + options.beforeLoaderConfig(configuration, loaderConfig); + } - if (nlsConfig.pseudo) { - amdRequire(['vs/nls'], function (nlsPlugin) { - nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); - }); + 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); } - if (options && typeof options.beforeRequire === 'function') { - options.beforeRequire(); + /** + * @returns {{[param: string]: string }} + */ + function parseURLQueryArgs() { + const search = window.location.search || ''; + + 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; }, {}); } - 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); - } - }, onUnexpectedError); -}; - -/** - * @returns {{[param: string]: string }} - */ -function parseURLQueryArgs() { - const search = window.location.search || ''; - - 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(''); - }; + /** + * @param {boolean} disallowReloadKeybinding + * @returns {() => void} + */ + function registerDeveloperKeybindings(disallowReloadKeybinding) { + const ipcRenderer = preloadGlobals.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 + // 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 + + 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 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'); - } - }; + window.addEventListener('keydown', listener); - window.addEventListener('keydown', listener); + return function () { + if (listener) { + window.removeEventListener('keydown', listener); + listener = undefined; + } + }; + } - return function () { - if (listener) { - window.removeEventListener('keydown', listener); - listener = undefined; + /** + * @param {string | Error} error + * @param {boolean} [enableDeveloperTools] + */ + function onUnexpectedError(error, enableDeveloperTools) { + if (enableDeveloperTools) { + const ipcRenderer = preloadGlobals.ipcRenderer; + ipcRenderer.send('vscode:openDevTools'); } - }; -} -function onUnexpectedError(error, enableDeveloperTools) { + console.error(`[uncaught exception]: ${error}`); - // @ts-ignore - const ipc = require('electron').ipcRenderer; + if (error && typeof error !== 'string' && error.stack) { + console.error(error.stack); + } + } - if (enableDeveloperTools) { - ipc.send('vscode:openDevTools'); + /** + * @return {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals')} + */ + function globals() { + // @ts-ignore (defined in globals.js) + return window.vscode; } - console.error('[uncaught exception]: ' + error); + /** + * TODO@sandbox this should not use the file:// protocol at all + * and be consolidated with the fileUriFromPath() method in + * bootstrap.js. + * + * @param {string} path + * @returns {string} + */ + function uriFromPath(path) { + let pathName = path.replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = `/${pathName}`; + } + + /** @type {string} */ + let uri; + if (safeProcess.platform === 'win32' && pathName.startsWith('//')) { // specially handle Windows UNC paths + uri = encodeURI(`file:${pathName}`); + } else { + uri = encodeURI(`file://${pathName}`); + } - if (error.stack) { - console.error(error.stack); + return uri.replace(/#/g, '%23'); } -} + + return { + load, + globals + }; +})); diff --git a/src/bootstrap.js b/src/bootstrap.js index c3815b745cc56..d1abd5502df63 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -6,281 +6,241 @@ //@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; + // Node.js + if (typeof exports === 'object') { + module.exports = factory(); + } -// 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')); -}); + // Browser + else { + try { + globalThis.MonacoBootstrap = factory(); + } catch (error) { + console.warn(error); // expected when e.g. running with sandbox: true (TODO@sandbox eventually consolidate this) + } + } +}(this, function () { + const Module = require('module'); + const path = require('path'); + const fs = require('fs'); -//#endregion + //#region global bootstrapping -//#region Add support for redirecting the loading of node modules + // increase number of stack frames(from 10, https://github.com/v8/v8/wiki/Stack-Trace-API) + Error.stackTraceLimit = 100; -exports.injectNodeModuleLookupPath = function (injectPath) { - if (!injectPath) { - throw new Error('Missing injectPath'); - } + // Workaround for Electron not installing a handler to ignore SIGPIPE + // (https://github.com/electron/electron/issues/13254) + process.on('SIGPIPE', () => { + console.error(new Error('Unexpected SIGPIPE')); + }); - // @ts-ignore - const Module = require('module'); - const path = require('path'); + //#endregion - const nodeModulesPath = path.join(__dirname, '../node_modules'); - // @ts-ignore - const originalResolveLookupPaths = Module._resolveLookupPaths; + //#region Add support for using node_modules.asar - // @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; - } + /** + * @param {string} appRoot + */ + function enableASARSupport(appRoot) { + 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); } } - return paths; - }; -}; -//#endregion + const NODE_MODULES_ASAR_PATH = `${NODE_MODULES_PATH}.asar`; -//#region Add support for using node_modules.asar -/** - * @param {string=} nodeModulesPath - */ -exports.enableASARSupport = function (nodeModulesPath) { + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; - // @ts-ignore - const Module = require('module'); - const path = require('path'); + // @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; + } + } + } - let NODE_MODULES_PATH = nodeModulesPath; - if (!NODE_MODULES_PATH) { - NODE_MODULES_PATH = path.join(__dirname, '../node_modules'); + return paths; + }; } - const NODE_MODULES_ASAR_PATH = NODE_MODULES_PATH + '.asar'; + //#endregion - // @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; - } - } + //#region URI helpers + + /** + * @param {string} _path + * @returns {string} + */ + function fileUriFromPath(_path) { + let pathName = path.resolve(_path).replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = `/${pathName}`; } - return paths; - }; -}; -//#endregion - -//#region URI helpers -/** - * @param {string} _path - * @returns {string} - */ -exports.uriFromPath = function (_path) { - const path = require('path'); + /** @type {string} */ + let uri; + if (process.platform === 'win32' && pathName.startsWith('//')) { // specially handle Windows UNC paths + uri = encodeURI(`file:${pathName}`); + } else { + uri = encodeURI(`file://${pathName}`); + } - let pathName = path.resolve(_path).replace(/\\/g, '/'); - if (pathName.length > 0 && pathName.charAt(0) !== '/') { - pathName = '/' + pathName; + return uri.replace(/#/g, '%23'); } - /** @type {string} */ - let uri; - if (process.platform === 'win32' && pathName.startsWith('//')) { // specially handle Windows UNC paths - uri = encodeURI('file:' + pathName); - } else { - uri = encodeURI('file://' + pathName); - } + //#endregion - return uri.replace(/#/g, '%23'); -}; -//#endregion -//#region FS helpers -/** - * @param {string} file - * @returns {Promise} - */ -exports.readFile = function (file) { - const fs = require('fs'); + //#region NLS helpers - return new Promise(function (resolve, reject) { - fs.readFile(file, 'utf8', function (err, data) { - if (err) { - reject(err); - return; - } - resolve(data); - }); - }); -}; - -/** - * @param {string} file - * @param {string} content - * @returns {Promise} - */ -exports.writeFile = function (file, content) { - const fs = require('fs'); + /** + * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} + */ + function setupNLS() { - return new Promise(function (resolve, reject) { - fs.writeFile(file, content, 'utf8', function (err) { - if (err) { - reject(err); - return; + // 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 } - resolve(); - }); - }); -}; + } -/** - * @param {string} dir - * @returns {Promise} - */ -exports.mkdirp = function mkdirp(dir) { - const fs = require('fs'); + if (nlsConfig._resolvedLanguagePackCoreLocation) { + const bundles = Object.create(null); - return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); -}; -//#endregion + nlsConfig.loadBundle = function (bundle, language, cb) { + const result = bundles[bundle]; + if (result) { + cb(undefined, result); -//#region NLS helpers -/** - * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean }} - */ -exports.setupNLS = function () { - const path = require('path'); + return; + } - // 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 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); + } + }); + }; } - } - if (nlsConfig._resolvedLanguagePackCoreLocation) { - const bundles = Object.create(null); + return nlsConfig; + } - nlsConfig.loadBundle = function (bundle, language, cb) { - const result = bundles[bundle]; - if (result) { - cb(undefined, result); + //#endregion - return; - } - 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; + //#region Portable helpers - cb(undefined, json); - }).catch((error) => { - try { - if (nlsConfig._corruptedFile) { - exports.writeFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); }); - } - } finally { - cb(error, undefined); - } - }); - }; - } + /** + * @param {{ portable: string; applicationName: string; }} product + * @returns {{portableDataPath: string;isPortable: boolean;}} + */ + function configurePortable(product) { + const appRoot = path.dirname(__dirname); - return nlsConfig; -}; -//#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'); + function getApplicationPath() { + if (process.env['VSCODE_DEV']) { + return appRoot; + } - const appRoot = path.dirname(__dirname); + if (process.platform === 'darwin') { + return path.dirname(path.dirname(path.dirname(appRoot))); + } - function getApplicationPath() { - if (process.env['VSCODE_DEV']) { - return appRoot; + return path.dirname(path.dirname(appRoot)); } - if (process.platform === 'darwin') { - return path.dirname(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); } - return path.dirname(path.dirname(appRoot)); - } + const portableDataPath = getPortableDataPath(); + const isPortable = !('target' in product) && fs.existsSync(portableDataPath); + const portableTempPath = path.join(portableDataPath, 'tmp'); + const isTempPortable = isPortable && fs.existsSync(portableTempPath); - function getPortableDataPath() { - if (process.env['VSCODE_PORTABLE']) { - return process.env['VSCODE_PORTABLE']; + if (isPortable) { + process.env['VSCODE_PORTABLE'] = portableDataPath; + } else { + delete process.env['VSCODE_PORTABLE']; } - if (process.platform === 'win32' || process.platform === 'linux') { - return path.join(getApplicationPath(), 'data'); + if (isTempPortable) { + if (process.platform === 'win32') { + process.env['TMP'] = portableTempPath; + process.env['TEMP'] = portableTempPath; + } else { + process.env['TMPDIR'] = portableTempPath; + } } - const portableDataName = product.portable || `${product.applicationName}-portable-data`; - return path.join(path.dirname(getApplicationPath()), portableDataName); + return { + portableDataPath, + isPortable + }; } - 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) { - process.env[process.platform === 'win32' ? 'TEMP' : 'TMPDIR'] = portableTempPath; + //#region ApplicationInsights + + // Prevents appinsights from monkey patching modules. + // This should be called before importing the applicationinsights module + function avoidMonkeyPatchFromAppInsights() { + // @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 2df9bf24e732f..f6d1c647d9dd3 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 4be0bad61d8aa..872cd66733d04 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 4c5990f105630..322fae7aca48f 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,65 @@ 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. + process.argv.push('--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 +107,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,8 +132,11 @@ registerListeners(); // Cached data const nodeCachedDataDir = getNodeCachedDir(); -// Configure static command line arguments -const argvConfig = configureCommandlineSwitchesSync(args); +// Remove env set by snap https://github.com/microsoft/vscode/issues/85344 +if (process.env['SNAP']) { + delete process.env['GDK_PIXBUF_MODULE_FILE']; + delete process.env['GDK_PIXBUF_MODULEDIR']; +} /** * Support user defined locale: load it early before app('ready') @@ -74,7 +155,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 +162,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 +200,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,27 +211,59 @@ 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.appendArgument(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.`); + } } - } else { - app.commandLine.appendSwitch(argvKey, argvValue); } }); @@ -201,21 +313,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', @@ -228,19 +329,10 @@ function createDefaultArgvConfigSync(argvConfigPath) { '', ' // 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' + ' "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) { @@ -263,7 +355,7 @@ function getArgvConfigPath() { } /** - * @param {ParsedArgs} cliArgs + * @param {NativeParsedArgs} cliArgs * @returns {string} */ function getJSFlags(cliArgs) { @@ -283,7 +375,7 @@ function getJSFlags(cliArgs) { } /** - * @param {ParsedArgs} cliArgs + * @param {NativeParsedArgs} cliArgs * * @returns {string} */ @@ -296,17 +388,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' ] }); } @@ -327,7 +420,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[]} @@ -339,7 +432,7 @@ function registerListeners() { }); /** - * React to open-url requests. + * macOS: react to open-url requests. * * @type {string[]} */ @@ -373,7 +466,7 @@ function getNodeCachedDir() { async ensureExists() { try { - await bootstrap.mkdirp(this.value); + await mkdirp(this.value); return this.value; } catch (error) { @@ -402,7 +495,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 * @@ -484,18 +590,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 33c691bf72b00..a88927d200b00 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 907c210b53038..6decaae056e49 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -2,20 +2,26 @@ "compilerOptions": { "module": "amd", "moduleResolution": "node", - "noImplicitAny": true, "experimentalDecorators": true, "noImplicitReturns": true, "noUnusedLocals": true, - "noImplicitThis": true, - "alwaysStrict": true, - "strictBindCallApply": true, - "strictNullChecks": 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 8917d57336d6a..5a2784b630ffd 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -4,13 +4,8 @@ "removeComments": false, "preserveConstEnums": true, "sourceMap": false, - "outDir": "../out", + "outDir": "../out/vs", "target": "es2017", - "lib": [ - "dom", - "es5", - "es2015.iterable" - ], "types": [ "keytar", "mocha", @@ -22,11 +17,5 @@ "include": [ "./typings", "./vs" - ], - "exclude": [ - "./typings/require-monaco.d.ts", - "./typings/xterm.d.ts", - "./typings/xterm-addon-search.d.ts", - "./typings/xterm-addon-web-links.d.ts" ] } diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index aa0af48b342b8..86b2926a1abd2 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -8,26 +8,19 @@ "moduleResolution": "classic", "removeComments": false, "preserveConstEnums": true, - "target": "es5", + "target": "es6", "sourceMap": false, "declaration": true }, "include": [ "typings/require.d.ts", - "typings/require-monaco.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/applicationInsights.d.ts b/src/typings/applicationInsights.d.ts deleted file mode 100644 index 5c6cb717a288c..0000000000000 --- a/src/typings/applicationInsights.d.ts +++ /dev/null @@ -1,218 +0,0 @@ -/** - * The singleton meta class for the default client of the client. This class is used to setup/start and configure - * the auto-collection behavior of the application insights module. - */ -declare module ApplicationInsights { - - /** - * The default client, initialized when setup was called. To initialize a different client - * with its own configuration, use `new TelemetryClient(instrumentationKey?)`. - */ - var defaultClient: TelemetryClient; - /** - * Initializes the default client. Should be called after setting - * configuration options. - * - * @param instrumentationKey the instrumentation key to use. Optional, if - * this is not specified, the value will be read from the environment - * variable APPINSIGHTS_INSTRUMENTATIONKEY. - * @returns {Configuration} the configuration class to initialize - * and start the SDK. - */ - function setup(instrumentationKey?: string): typeof Configuration; - /** - * Starts automatic collection of telemetry. Prior to calling start no - * telemetry will be *automatically* collected, though manual collection - * is enabled. - * @returns {ApplicationInsights} this class - */ - function start(): typeof Configuration; - /** - * The active configuration for global SDK behaviors, such as autocollection. - */ - class Configuration { - static start: typeof start; - /** - * Sets the state of console and logger tracking (enabled by default for third-party loggers only) - * @param value if true logger activity will be sent to Application Insights - * @param collectConsoleLog if true, logger autocollection will include console.log calls (default false) - * @returns {Configuration} this class - */ - static setAutoCollectConsole(value: boolean, collectConsoleLog?: boolean): typeof Configuration; - /** - * Sets the state of exception tracking (enabled by default) - * @param value if true uncaught exceptions will be sent to Application Insights - * @returns {Configuration} this class - */ - static setAutoCollectExceptions(value: boolean): typeof Configuration; - /** - * Sets the state of performance tracking (enabled by default) - * @param value if true performance counters will be collected every second and sent to Application Insights - * @returns {Configuration} this class - */ - static setAutoCollectPerformance(value: boolean): typeof Configuration; - /** - * Sets the state of request tracking (enabled by default) - * @param value if true requests will be sent to Application Insights - * @returns {Configuration} this class - */ - static setAutoCollectRequests(value: boolean): typeof Configuration; - /** - * Sets the state of dependency tracking (enabled by default) - * @param value if true dependencies will be sent to Application Insights - * @returns {Configuration} this class - */ - static setAutoCollectDependencies(value: boolean): typeof Configuration; - /** - * Sets the state of automatic dependency correlation (enabled by default) - * @param value if true dependencies will be correlated with requests - * @returns {Configuration} this class - */ - static setAutoDependencyCorrelation(value: boolean): typeof Configuration; - /** - * Enable or disable disk-backed retry caching to cache events when client is offline (enabled by default) - * Note that this method only applies to the default client. Disk-backed retry caching is disabled by default for additional clients. - * For enable for additional clients, use client.channel.setUseDiskRetryCaching(true). - * These cached events are stored in your system or user's temporary directory and access restricted to your user when possible. - * @param value if true events that occured while client is offline will be cached on disk - * @param resendInterval The wait interval for resending cached events. - * @param maxBytesOnDisk The maximum size (in bytes) that the created temporary directory for cache events can grow to, before caching is disabled. - * @returns {Configuration} this class - */ - static setUseDiskRetryCaching(value: boolean, resendInterval?: number, maxBytesOnDisk?: number): typeof Configuration; - /** - * Enables debug and warning logging for AppInsights itself. - * @param enableDebugLogging if true, enables debug logging - * @param enableWarningLogging if true, enables warning logging - * @returns {Configuration} this class - */ - static setInternalLogging(enableDebugLogging?: boolean, enableWarningLogging?: boolean): typeof Configuration; - } - /** - * Disposes the default client and all the auto collectors so they can be reinitialized with different configuration - */ - function dispose(): void; - - interface ITelemetryClient { - config: Config; - channel: Channel; - /** - * Log a user action or other occurrence. - * @param telemetry Object encapsulating tracking options - */ - trackEvent(telemetry: EventTelemetry): void; - /** - * Immediately send all queued telemetry. - * @param options Flush options, including indicator whether app is crashing and callback - */ - flush(options?: FlushOptions): void; - - } - - class TelemetryClient implements ITelemetryClient { - config: Config; - channel: Channel; - /** - * Constructs a new client of the client - * @param iKey the instrumentation key to use (read from environment variable if not specified) - */ - constructor(iKey?: string); - /** - * Log a user action or other occurrence. - * @param telemetry Object encapsulating tracking options - */ - trackEvent(telemetry: EventTelemetry): void; - /** - * Immediately send all queued telemetry. - * @param options Flush options, including indicator whether app is crashing and callback - */ - flush(options?: FlushOptions): void; - - } - - class Config { - static ENV_azurePrefix: string; - static ENV_iKey: string; - static legacy_ENV_iKey: string; - static ENV_profileQueryEndpoint: string; - static ENV_http_proxy: string; - static ENV_https_proxy: string; - /** An identifier for your Application Insights resource */ - instrumentationKey: string; - /** The id for cross-component correlation. READ ONLY. */ - correlationId: string; - /** The ingestion endpoint to send telemetry payloads to */ - endpointUrl: string; - /** The maximum number of telemetry items to include in a payload to the ingestion endpoint (Default 250) */ - maxBatchSize: number; - /** The maximum amount of time to wait for a payload to reach maxBatchSize (Default 15000) */ - maxBatchIntervalMs: number; - /** A flag indicating if telemetry transmission is disabled (Default false) */ - disableAppInsights: boolean; - /** The percentage of telemetry items tracked that should be transmitted (Default 100) */ - samplingPercentage: number; - /** The time to wait before retrying to retrieve the id for cross-component correlation (Default 30000) */ - correlationIdRetryIntervalMs: number; - /** A list of domains to exclude from cross-component header injection */ - correlationHeaderExcludedDomains: string[]; - /** A proxy server for SDK HTTP traffic (Optional, Default pulled from `http_proxy` environment variable) */ - proxyHttpUrl: string; - /** A proxy server for SDK HTTPS traffic (Optional, Default pulled from `https_proxy` environment variable) */ - proxyHttpsUrl: string; - } - - interface Channel { - /** - * Enable or disable disk-backed retry caching to cache events when client is offline (enabled by default) - * These cached events are stored in your system or user's temporary directory and access restricted to your user when possible. - * @param value if true events that occured while client is offline will be cached on disk - * @param resendInterval The wait interval for resending cached events. - * @param maxBytesOnDisk The maximum size (in bytes) that the created temporary directory for cache events can grow to, before caching is disabled. - * @returns {Configuration} this class - */ - setUseDiskRetryCaching(value: boolean, resendInterval?: number, maxBytesOnDisk?: number): void; - } - - /** - * Telemetry about the custom event of interest, such application workflow event, business logic event (purchase) and anything that - * you would like to track and aggregate by count. Event can contain measurements such as purchase amount associated with purchase event - */ - interface EventTelemetry { - /** - * Name of the event - */ - name: string; - /** - * Metrics associated with this event, displayed in Metrics Explorer on the portal. - */ - measurements?: { - [key: string]: number; - }; - /** - * Additional data used to filter events and metrics in the portal. Defaults to empty. - */ - properties?: { - [key: string]: string; - }; - } - - /** - * Encapsulates options passed into client.flush() function - */ - interface FlushOptions { - /** - * Flag indicating whether application is crashing. When this flag is set to true - * and storing data locally is enabled, Node.JS SDK will attempt to store data on disk - */ - isAppCrashing?: boolean; - /** - * Callback that will be invoked with the response from server, in case of isAppCrashing set to true, - * with immediate notification that data was stored - */ - callback?: (v: string) => void; - } -} - -declare module 'applicationinsights' { - export = ApplicationInsights; -} diff --git a/src/typings/applicationinsights-web.d.ts b/src/typings/applicationinsights-web.d.ts deleted file mode 100644 index 5af4903525c72..0000000000000 --- a/src/typings/applicationinsights-web.d.ts +++ /dev/null @@ -1,59 +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 module '@microsoft/applicationinsights-web' { - export interface IConfig { - instrumentationKey?: string; - endpointUrl?: string; - emitLineDelimitedJson?: boolean; - accountId?: string; - sessionRenewalMs?: number; - sessionExpirationMs?: number; - maxBatchSizeInBytes?: number; - maxBatchInterval?: number; - enableDebug?: boolean; - disableExceptionTracking?: boolean; - disableTelemetry?: boolean; - verboseLogging?: boolean; - diagnosticLogInterval?: number; - samplingPercentage?: number; - autoTrackPageVisitTime?: boolean; - disableAjaxTracking?: boolean; - overridePageViewDuration?: boolean; - maxAjaxCallsPerView?: number; - disableDataLossAnalysis?: boolean; - disableCorrelationHeaders?: boolean; - correlationHeaderExcludedDomains?: string[]; - disableFlushOnBeforeUnload?: boolean; - enableSessionStorageBuffer?: boolean; - isCookieUseDisabled?: boolean; - cookieDomain?: string; - isRetryDisabled?: boolean; - url?: string; - isStorageUseDisabled?: boolean; - isBeaconApiDisabled?: boolean; - sdkExtension?: string; - isBrowserLinkTrackingEnabled?: boolean; - appId?: string; - enableCorsCorrelation?: boolean; - } - - export interface ISnippet { - config: IConfig; - } - - export interface IEventTelemetry { - name: string; - properties?: { [key: string]: string }; - measurements?: { [key: string]: number }; - } - - export class ApplicationInsights { - constructor(config: ISnippet); - loadAppInsights(): void; - trackEvent(data: IEventTelemetry): void; - flush(): void; - } -} diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts deleted file mode 100644 index 6d90d8c0b1aab..0000000000000 --- a/src/typings/electron.d.ts +++ /dev/null @@ -1,10730 +0,0 @@ -// Type definitions for Electron 6.1.2 -// Project: http://electronjs.org/ -// Definitions by: The Electron Team -// Definitions: https://github.com/electron/electron-typescript-definitions - -/// - -type GlobalEvent = Event; - -declare namespace Electron { - // TODO: Replace this declaration with NodeJS.EventEmitter - class EventEmitter { - addListener(event: string, listener: Function): this; - on(event: string, listener: Function): this; - once(event: string, listener: Function): this; - removeListener(event: string, listener: Function): this; - removeAllListeners(event?: string): this; - setMaxListeners(n: number): this; - getMaxListeners(): number; - listeners(event: string): Function[]; - emit(event: string, ...args: any[]): boolean; - listenerCount(type: string): number; - prependListener(event: string, listener: Function): this; - prependOnceListener(event: string, listener: Function): this; - eventNames(): Array<(string | symbol)>; - } - - class Accelerator extends String { - - } - - interface CommonInterface { - clipboard: Clipboard; - crashReporter: CrashReporter; - nativeImage: typeof NativeImage; - shell: Shell; - } - - interface MainInterface extends CommonInterface { - app: App; - autoUpdater: AutoUpdater; - BrowserView: typeof BrowserView; - BrowserWindow: typeof BrowserWindow; - ClientRequest: typeof ClientRequest; - contentTracing: ContentTracing; - Cookies: typeof Cookies; - Debugger: typeof Debugger; - dialog: Dialog; - DownloadItem: typeof DownloadItem; - globalShortcut: GlobalShortcut; - inAppPurchase: InAppPurchase; - IncomingMessage: typeof IncomingMessage; - ipcMain: IpcMain; - Menu: typeof Menu; - MenuItem: typeof MenuItem; - net: Net; - netLog: NetLog; - Notification: typeof Notification; - powerMonitor: PowerMonitor; - powerSaveBlocker: PowerSaveBlocker; - protocol: Protocol; - screen: Screen; - session: typeof Session; - systemPreferences: SystemPreferences; - TouchBar: typeof TouchBar; - Tray: typeof Tray; - webContents: typeof WebContents; - WebRequest: typeof WebRequest; - } - - interface RendererInterface extends CommonInterface { - BrowserWindowProxy: typeof BrowserWindowProxy; - contextBridge: ContextBridge; - desktopCapturer: DesktopCapturer; - ipcRenderer: IpcRenderer; - remote: Remote; - webFrame: WebFrame; - webviewTag: WebviewTag; - } - - interface AllElectron extends MainInterface, RendererInterface { } - - const app: App; - const autoUpdater: AutoUpdater; - const clipboard: Clipboard; - const contentTracing: ContentTracing; - const contextBridge: ContextBridge; - const crashReporter: CrashReporter; - const desktopCapturer: DesktopCapturer; - const dialog: Dialog; - const globalShortcut: GlobalShortcut; - const inAppPurchase: InAppPurchase; - const ipcMain: IpcMain; - const ipcRenderer: IpcRenderer; - type nativeImage = NativeImage; - const nativeImage: typeof NativeImage; - const net: Net; - const netLog: NetLog; - const powerMonitor: PowerMonitor; - const powerSaveBlocker: PowerSaveBlocker; - const protocol: Protocol; - // const remote: Remote; ### VSCODE CHANGE (we do not want to use remote) - const screen: Screen; - type session = Session; - const session: typeof Session; - const shell: Shell; - const systemPreferences: SystemPreferences; - type webContents = WebContents; - const webContents: typeof WebContents; - const webFrame: WebFrame; - const webviewTag: WebviewTag; - - interface App extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/app - - /** - * Emitted when Chrome's accessibility support changes. This event fires when - * assistive technologies, such as screen readers, are enabled or disabled. See - * https://www.chromium.org/developers/design-documents/accessibility for more - * details. - */ - on(event: 'accessibility-support-changed', listener: (event: Event, - /** - * `true` when Chrome's accessibility support is enabled, `false` otherwise. - */ - accessibilitySupportEnabled: boolean) => void): this; - once(event: 'accessibility-support-changed', listener: (event: Event, - /** - * `true` when Chrome's accessibility support is enabled, `false` otherwise. - */ - accessibilitySupportEnabled: boolean) => void): this; - addListener(event: 'accessibility-support-changed', listener: (event: Event, - /** - * `true` when Chrome's accessibility support is enabled, `false` otherwise. - */ - accessibilitySupportEnabled: boolean) => void): this; - removeListener(event: 'accessibility-support-changed', listener: (event: Event, - /** - * `true` when Chrome's accessibility support is enabled, `false` otherwise. - */ - accessibilitySupportEnabled: boolean) => void): this; - /** - * Emitted when the application is activated. Various actions can trigger this - * event, such as launching the application for the first time, attempting to - * re-launch the application when it's already running, or clicking on the - * application's dock or taskbar icon. - */ - on(event: 'activate', listener: (event: Event, - hasVisibleWindows: boolean) => void): this; - once(event: 'activate', listener: (event: Event, - hasVisibleWindows: boolean) => void): this; - addListener(event: 'activate', listener: (event: Event, - hasVisibleWindows: boolean) => void): this; - removeListener(event: 'activate', listener: (event: Event, - hasVisibleWindows: boolean) => void): this; - /** - * Emitted during Handoff after an activity from this device was successfully - * resumed on another one. - */ - on(event: 'activity-was-continued', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - once(event: 'activity-was-continued', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - addListener(event: 'activity-was-continued', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - removeListener(event: 'activity-was-continued', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - /** - * Emitted before the application starts closing its windows. Calling - * event.preventDefault() will prevent the default behavior, which is terminating - * the application. Note: If application quit was initiated by - * autoUpdater.quitAndInstall(), then before-quit is emitted after emitting close - * event on all windows and closing them. Note: On Windows, this event will not be - * emitted if the app is closed due to a shutdown/restart of the system or a user - * logout. - */ - on(event: 'before-quit', listener: (event: Event) => void): this; - once(event: 'before-quit', listener: (event: Event) => void): this; - addListener(event: 'before-quit', listener: (event: Event) => void): this; - removeListener(event: 'before-quit', listener: (event: Event) => void): this; - /** - * Emitted when a browserWindow gets blurred. - */ - on(event: 'browser-window-blur', listener: (event: Event, - window: BrowserWindow) => void): this; - once(event: 'browser-window-blur', listener: (event: Event, - window: BrowserWindow) => void): this; - addListener(event: 'browser-window-blur', listener: (event: Event, - window: BrowserWindow) => void): this; - removeListener(event: 'browser-window-blur', listener: (event: Event, - window: BrowserWindow) => void): this; - /** - * Emitted when a new browserWindow is created. - */ - on(event: 'browser-window-created', listener: (event: Event, - window: BrowserWindow) => void): this; - once(event: 'browser-window-created', listener: (event: Event, - window: BrowserWindow) => void): this; - addListener(event: 'browser-window-created', listener: (event: Event, - window: BrowserWindow) => void): this; - removeListener(event: 'browser-window-created', listener: (event: Event, - window: BrowserWindow) => void): this; - /** - * Emitted when a browserWindow gets focused. - */ - on(event: 'browser-window-focus', listener: (event: Event, - window: BrowserWindow) => void): this; - once(event: 'browser-window-focus', listener: (event: Event, - window: BrowserWindow) => void): this; - addListener(event: 'browser-window-focus', listener: (event: Event, - window: BrowserWindow) => void): this; - removeListener(event: 'browser-window-focus', listener: (event: Event, - window: BrowserWindow) => void): this; - /** - * Emitted when failed to verify the certificate for url, to trust the certificate - * you should prevent the default behavior with event.preventDefault() and call - * callback(true). - */ - on(event: 'certificate-error', listener: (event: Event, - webContents: WebContents, - url: string, - /** - * The error code - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - once(event: 'certificate-error', listener: (event: Event, - webContents: WebContents, - url: string, - /** - * The error code - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - addListener(event: 'certificate-error', listener: (event: Event, - webContents: WebContents, - url: string, - /** - * The error code - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - removeListener(event: 'certificate-error', listener: (event: Event, - webContents: WebContents, - url: string, - /** - * The error code - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - /** - * Emitted during Handoff when an activity from a different device wants to be - * resumed. You should call event.preventDefault() if you want to handle this - * event. A user activity can be continued only in an app that has the same - * developer Team ID as the activity's source app and that supports the activity's - * type. Supported activity types are specified in the app's Info.plist under the - * NSUserActivityTypes key. - */ - on(event: 'continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity on another device. - */ - userInfo: any) => void): this; - once(event: 'continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity on another device. - */ - userInfo: any) => void): this; - addListener(event: 'continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity on another device. - */ - userInfo: any) => void): this; - removeListener(event: 'continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity on another device. - */ - userInfo: any) => void): this; - /** - * Emitted during Handoff when an activity from a different device fails to be - * resumed. - */ - on(event: 'continue-activity-error', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * A string with the error's localized description. - */ - error: string) => void): this; - once(event: 'continue-activity-error', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * A string with the error's localized description. - */ - error: string) => void): this; - addListener(event: 'continue-activity-error', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * A string with the error's localized description. - */ - error: string) => void): this; - removeListener(event: 'continue-activity-error', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * A string with the error's localized description. - */ - error: string) => void): this; - /** - * Emitted when desktopCapturer.getSources() is called in the renderer process of - * webContents. Calling event.preventDefault() will make it return empty sources. - */ - on(event: 'desktop-capturer-get-sources', listener: (event: Event, - webContents: WebContents) => void): this; - once(event: 'desktop-capturer-get-sources', listener: (event: Event, - webContents: WebContents) => void): this; - addListener(event: 'desktop-capturer-get-sources', listener: (event: Event, - webContents: WebContents) => void): this; - removeListener(event: 'desktop-capturer-get-sources', listener: (event: Event, - webContents: WebContents) => void): this; - /** - * Emitted when the gpu process crashes or is killed. - */ - on(event: 'gpu-process-crashed', listener: (event: Event, - killed: boolean) => void): this; - once(event: 'gpu-process-crashed', listener: (event: Event, - killed: boolean) => void): this; - addListener(event: 'gpu-process-crashed', listener: (event: Event, - killed: boolean) => void): this; - removeListener(event: 'gpu-process-crashed', listener: (event: Event, - killed: boolean) => void): this; - /** - * Emitted when webContents wants to do basic auth. The default behavior is to - * cancel all authentications. To override this you should prevent the default - * behavior with event.preventDefault() and call callback(username, password) with - * the credentials. - */ - on(event: 'login', listener: (event: Event, - webContents: WebContents, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - once(event: 'login', listener: (event: Event, - webContents: WebContents, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - addListener(event: 'login', listener: (event: Event, - webContents: WebContents, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - removeListener(event: 'login', listener: (event: Event, - webContents: WebContents, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - /** - * Emitted when the user clicks the native macOS new tab button. The new tab button - * is only visible if the current BrowserWindow has a tabbingIdentifier - */ - on(event: 'new-window-for-tab', listener: (event: Event) => void): this; - once(event: 'new-window-for-tab', listener: (event: Event) => void): this; - addListener(event: 'new-window-for-tab', listener: (event: Event) => void): this; - removeListener(event: 'new-window-for-tab', listener: (event: Event) => void): this; - /** - * Emitted when the user wants to open a file with the application. The open-file - * event is usually emitted when the application is already open and the OS wants - * to reuse the application to open the file. open-file is also emitted when a file - * is dropped onto the dock and the application is not yet running. Make sure to - * listen for the open-file event very early in your application startup to handle - * this case (even before the ready event is emitted). You should call - * event.preventDefault() if you want to handle this event. On Windows, you have to - * parse process.argv (in the main process) to get the filepath. - */ - on(event: 'open-file', listener: (event: Event, - path: string) => void): this; - once(event: 'open-file', listener: (event: Event, - path: string) => void): this; - addListener(event: 'open-file', listener: (event: Event, - path: string) => void): this; - removeListener(event: 'open-file', listener: (event: Event, - path: string) => void): this; - /** - * Emitted when the user wants to open a URL with the application. Your - * application's Info.plist file must define the url scheme within the - * CFBundleURLTypes key, and set NSPrincipalClass to AtomApplication. You should - * call event.preventDefault() if you want to handle this event. - */ - on(event: 'open-url', listener: (event: Event, - url: string) => void): this; - once(event: 'open-url', listener: (event: Event, - url: string) => void): this; - addListener(event: 'open-url', listener: (event: Event, - url: string) => void): this; - removeListener(event: 'open-url', listener: (event: Event, - url: string) => void): this; - /** - * Emitted when the application is quitting. Note: On Windows, this event will not - * be emitted if the app is closed due to a shutdown/restart of the system or a - * user logout. - */ - on(event: 'quit', listener: (event: Event, - exitCode: number) => void): this; - once(event: 'quit', listener: (event: Event, - exitCode: number) => void): this; - addListener(event: 'quit', listener: (event: Event, - exitCode: number) => void): this; - removeListener(event: 'quit', listener: (event: Event, - exitCode: number) => void): this; - /** - * Emitted when Electron has finished initializing. On macOS, launchInfo holds the - * userInfo of the NSUserNotification that was used to open the application, if it - * was launched from Notification Center. You can call app.isReady() to check if - * this event has already fired. - */ - on(event: 'ready', listener: (launchInfo: any) => void): this; - once(event: 'ready', listener: (launchInfo: any) => void): this; - addListener(event: 'ready', listener: (launchInfo: any) => void): this; - removeListener(event: 'ready', listener: (launchInfo: any) => void): this; - /** - * Emitted when remote.getBuiltin() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the module from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-builtin', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - once(event: 'remote-get-builtin', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - addListener(event: 'remote-get-builtin', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - removeListener(event: 'remote-get-builtin', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - /** - * Emitted when remote.getCurrentWebContents() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the object from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-current-web-contents', listener: (event: Event, - webContents: WebContents) => void): this; - once(event: 'remote-get-current-web-contents', listener: (event: Event, - webContents: WebContents) => void): this; - addListener(event: 'remote-get-current-web-contents', listener: (event: Event, - webContents: WebContents) => void): this; - removeListener(event: 'remote-get-current-web-contents', listener: (event: Event, - webContents: WebContents) => void): this; - /** - * Emitted when remote.getCurrentWindow() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the object from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-current-window', listener: (event: Event, - webContents: WebContents) => void): this; - once(event: 'remote-get-current-window', listener: (event: Event, - webContents: WebContents) => void): this; - addListener(event: 'remote-get-current-window', listener: (event: Event, - webContents: WebContents) => void): this; - removeListener(event: 'remote-get-current-window', listener: (event: Event, - webContents: WebContents) => void): this; - /** - * Emitted when remote.getGlobal() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the global from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-global', listener: (event: Event, - webContents: WebContents, - globalName: string) => void): this; - once(event: 'remote-get-global', listener: (event: Event, - webContents: WebContents, - globalName: string) => void): this; - addListener(event: 'remote-get-global', listener: (event: Event, - webContents: WebContents, - globalName: string) => void): this; - removeListener(event: 'remote-get-global', listener: (event: Event, - webContents: WebContents, - globalName: string) => void): this; - /** - * Emitted when .getWebContents() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the object from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-guest-web-contents', listener: (event: Event, - webContents: WebContents, - guestWebContents: WebContents) => void): this; - once(event: 'remote-get-guest-web-contents', listener: (event: Event, - webContents: WebContents, - guestWebContents: WebContents) => void): this; - addListener(event: 'remote-get-guest-web-contents', listener: (event: Event, - webContents: WebContents, - guestWebContents: WebContents) => void): this; - removeListener(event: 'remote-get-guest-web-contents', listener: (event: Event, - webContents: WebContents, - guestWebContents: WebContents) => void): this; - /** - * Emitted when remote.require() is called in the renderer process of webContents. - * Calling event.preventDefault() will prevent the module from being returned. - * Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-require', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - once(event: 'remote-require', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - addListener(event: 'remote-require', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - removeListener(event: 'remote-require', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - /** - * Emitted when the renderer process of webContents crashes or is killed. - */ - on(event: 'renderer-process-crashed', listener: (event: Event, - webContents: WebContents, - killed: boolean) => void): this; - once(event: 'renderer-process-crashed', listener: (event: Event, - webContents: WebContents, - killed: boolean) => void): this; - addListener(event: 'renderer-process-crashed', listener: (event: Event, - webContents: WebContents, - killed: boolean) => void): this; - removeListener(event: 'renderer-process-crashed', listener: (event: Event, - webContents: WebContents, - killed: boolean) => void): this; - /** - * This event will be emitted inside the primary instance of your application when - * a second instance has been executed and calls app.requestSingleInstanceLock(). - * argv is an Array of the second instance's command line arguments, and - * workingDirectory is its current working directory. Usually applications respond - * to this by making their primary window focused and non-minimized. This event is - * guaranteed to be emitted after the ready event of app gets emitted. Note: Extra - * command line arguments might be added by Chromium, such as - * --original-process-start-time. - */ - on(event: 'second-instance', listener: (event: Event, - /** - * An array of the second instance's command line arguments - */ - argv: string[], - /** - * The second instance's working directory - */ - workingDirectory: string) => void): this; - once(event: 'second-instance', listener: (event: Event, - /** - * An array of the second instance's command line arguments - */ - argv: string[], - /** - * The second instance's working directory - */ - workingDirectory: string) => void): this; - addListener(event: 'second-instance', listener: (event: Event, - /** - * An array of the second instance's command line arguments - */ - argv: string[], - /** - * The second instance's working directory - */ - workingDirectory: string) => void): this; - removeListener(event: 'second-instance', listener: (event: Event, - /** - * An array of the second instance's command line arguments - */ - argv: string[], - /** - * The second instance's working directory - */ - workingDirectory: string) => void): this; - /** - * Emitted when a client certificate is requested. The url corresponds to the - * navigation entry requesting the client certificate and callback can be called - * with an entry filtered from the list. Using event.preventDefault() prevents the - * application from using the first certificate from the store. - */ - on(event: 'select-client-certificate', listener: (event: Event, - webContents: WebContents, - url: string, - certificateList: Certificate[], - callback: (certificate?: Certificate) => void) => void): this; - once(event: 'select-client-certificate', listener: (event: Event, - webContents: WebContents, - url: string, - certificateList: Certificate[], - callback: (certificate?: Certificate) => void) => void): this; - addListener(event: 'select-client-certificate', listener: (event: Event, - webContents: WebContents, - url: string, - certificateList: Certificate[], - callback: (certificate?: Certificate) => void) => void): this; - removeListener(event: 'select-client-certificate', listener: (event: Event, - webContents: WebContents, - url: string, - certificateList: Certificate[], - callback: (certificate?: Certificate) => void) => void): this; - /** - * Emitted when Electron has created a new session. - */ - on(event: 'session-created', listener: (session: Session) => void): this; - once(event: 'session-created', listener: (session: Session) => void): this; - addListener(event: 'session-created', listener: (session: Session) => void): this; - removeListener(event: 'session-created', listener: (session: Session) => void): this; - /** - * Emitted when Handoff is about to be resumed on another device. If you need to - * update the state to be transferred, you should call event.preventDefault() - * immediately, construct a new userInfo dictionary and call - * app.updateCurrentActiviy() in a timely manner. Otherwise, the operation will - * fail and continue-activity-error will be called. - */ - on(event: 'update-activity-state', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - once(event: 'update-activity-state', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - addListener(event: 'update-activity-state', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - removeListener(event: 'update-activity-state', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - /** - * Emitted when a new webContents is created. - */ - on(event: 'web-contents-created', listener: (event: Event, - webContents: WebContents) => void): this; - once(event: 'web-contents-created', listener: (event: Event, - webContents: WebContents) => void): this; - addListener(event: 'web-contents-created', listener: (event: Event, - webContents: WebContents) => void): this; - removeListener(event: 'web-contents-created', listener: (event: Event, - webContents: WebContents) => void): this; - /** - * Emitted during Handoff before an activity from a different device wants to be - * resumed. You should call event.preventDefault() if you want to handle this - * event. - */ - on(event: 'will-continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string) => void): this; - once(event: 'will-continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string) => void): this; - addListener(event: 'will-continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string) => void): this; - removeListener(event: 'will-continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string) => void): this; - /** - * Emitted when the application has finished basic startup. On Windows and Linux, - * the will-finish-launching event is the same as the ready event; on macOS, this - * event represents the applicationWillFinishLaunching notification of - * NSApplication. You would usually set up listeners for the open-file and open-url - * events here, and start the crash reporter and auto updater. In most cases, you - * should do everything in the ready event handler. - */ - on(event: 'will-finish-launching', listener: Function): this; - once(event: 'will-finish-launching', listener: Function): this; - addListener(event: 'will-finish-launching', listener: Function): this; - removeListener(event: 'will-finish-launching', listener: Function): this; - /** - * Emitted when all windows have been closed and the application will quit. Calling - * event.preventDefault() will prevent the default behaviour, which is terminating - * the application. See the description of the window-all-closed event for the - * differences between the will-quit and window-all-closed events. Note: On - * Windows, this event will not be emitted if the app is closed due to a - * shutdown/restart of the system or a user logout. - */ - on(event: 'will-quit', listener: (event: Event) => void): this; - once(event: 'will-quit', listener: (event: Event) => void): this; - addListener(event: 'will-quit', listener: (event: Event) => void): this; - removeListener(event: 'will-quit', listener: (event: Event) => void): this; - /** - * Emitted when all windows have been closed. If you do not subscribe to this event - * and all windows are closed, the default behavior is to quit the app; however, if - * you subscribe, you control whether the app quits or not. If the user pressed Cmd - * + Q, or the developer called app.quit(), Electron will first try to close all - * the windows and then emit the will-quit event, and in this case the - * window-all-closed event would not be emitted. - */ - on(event: 'window-all-closed', listener: Function): this; - once(event: 'window-all-closed', listener: Function): this; - addListener(event: 'window-all-closed', listener: Function): this; - removeListener(event: 'window-all-closed', listener: Function): this; - /** - * Adds path to the recent documents list. This list is managed by the OS. On - * Windows, you can visit the list from the task bar, and on macOS, you can visit - * it from dock menu. - */ - addRecentDocument(path: string): void; - /** - * Clears the recent documents list. - */ - clearRecentDocuments(): void; - /** - * By default, Chromium disables 3D APIs (e.g. WebGL) until restart on a per domain - * basis if the GPU processes crashes too frequently. This function disables that - * behaviour. This method can only be called before app is ready. - */ - disableDomainBlockingFor3DAPIs(): void; - /** - * Disables hardware acceleration for current app. This method can only be called - * before app is ready. - */ - disableHardwareAcceleration(): void; - /** - * Enables full sandbox mode on the app. This method can only be called before app - * is ready. - */ - enableSandbox(): void; - /** - * Exits immediately with exitCode. exitCode defaults to 0. All windows will be - * closed immediately without asking the user, and the before-quit and will-quit - * events will not be emitted. - */ - exit(exitCode?: number): void; - /** - * On Linux, focuses on the first visible window. On macOS, makes the application - * the active app. On Windows, focuses on the application's first window. - */ - focus(): void; - getAppMetrics(): ProcessMetric[]; - getAppPath(): string; - getBadgeCount(): number; - getCurrentActivityType(): string; - /** - * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux - * and macOS, icons depend on the application associated with file mime type. - */ - getFileIcon(path: string, options?: FileIconOptions): Promise; - /** - * Fetches a path's associated icon. On Windows, there are 2 kinds of icons: On - * Linux and macOS, icons depend on the application associated with file mime type. - * Deprecated Soon - */ - getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; - /** - * Fetches a path's associated icon. On Windows, there are 2 kinds of icons: On - * Linux and macOS, icons depend on the application associated with file mime type. - * Deprecated Soon - */ - getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; - getGPUFeatureStatus(): GPUFeatureStatus; - /** - * For infoType equal to complete: Promise is fulfilled with Object containing all - * the GPU Information as in chromium's GPUInfo object. This includes the version - * and driver information that's shown on chrome://gpu page. For infoType equal to - * basic: Promise is fulfilled with Object containing fewer attributes than when - * requested with complete. Here's an example of basic response: Using basic should - * be preferred if only basic information like vendorId or driverId is needed. - */ - getGPUInfo(infoType: string): Promise; - getJumpListSettings(): JumpListSettings; - /** - * To set the locale, you'll want to use a command line switch at app startup, - * which may be found here. Note: When distributing your packaged app, you have to - * also ship the locales folder. Note: On Windows, you have to call it after the - * ready events gets emitted. - */ - getLocale(): string; - /** - * Note: When unable to detect locale country code, it returns empty string. - */ - getLocaleCountryCode(): string; - /** - * If you provided path and args options to app.setLoginItemSettings, then you need - * to pass the same arguments here for openAtLogin to be set correctly. - */ - getLoginItemSettings(options?: LoginItemSettingsOptions): LoginItemSettings; - /** - * Usually the name field of package.json is a short lowercased name, according to - * the npm modules spec. You should usually also specify a productName field, which - * is your application's full capitalized name, and which will be preferred over - * name by Electron. - */ - getName(): string; - /** - * You can request the following paths by the name: - */ - getPath(name: string): string; - getVersion(): string; - /** - * This method returns whether or not this instance of your app is currently - * holding the single instance lock. You can request the lock with - * app.requestSingleInstanceLock() and release with app.releaseSingleInstanceLock() - */ - hasSingleInstanceLock(): boolean; - /** - * Hides all application windows without minimizing them. - */ - hide(): void; - /** - * Imports the certificate in pkcs12 format into the platform certificate store. - * callback is called with the result of import operation, a value of 0 indicates - * success while any other value indicates failure according to Chromium - * net_error_list. - */ - importCertificate(options: ImportCertificateOptions, callback: (result: number) => void): void; - /** - * Invalidates the current Handoff user activity. - */ - invalidateCurrentActivity(type: string): void; - /** - * Deprecated Soon - */ - isAccessibilitySupportEnabled(): boolean; - /** - * This method checks if the current executable is the default handler for a - * protocol (aka URI scheme). If so, it will return true. Otherwise, it will return - * false. Note: On macOS, you can use this method to check if the app has been - * registered as the default protocol handler for a protocol. You can also verify - * this by checking ~/Library/Preferences/com.apple.LaunchServices.plist on the - * macOS machine. Please refer to Apple's documentation for details. The API uses - * the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. - */ - isDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; - isEmojiPanelSupported(): boolean; - isInApplicationsFolder(): boolean; - isReady(): boolean; - isUnityRunning(): boolean; - /** - * No confirmation dialog will be presented by default. If you wish to allow the - * user to confirm the operation, you may do so using the dialog API. NOTE: This - * method throws errors if anything other than the user causes the move to fail. - * For instance if the user cancels the authorization dialog, this method returns - * false. If we fail to perform the copy, then this method will throw an error. The - * message in the error should be informative and tell you exactly what went wrong - */ - moveToApplicationsFolder(): boolean; - /** - * Try to close all windows. The before-quit event will be emitted first. If all - * windows are successfully closed, the will-quit event will be emitted and by - * default the application will terminate. This method guarantees that all - * beforeunload and unload event handlers are correctly executed. It is possible - * that a window cancels the quitting by returning false in the beforeunload event - * handler. - */ - quit(): void; - /** - * Relaunches the app when current instance exits. By default, the new instance - * will use the same working directory and command line arguments with current - * instance. When args is specified, the args will be passed as command line - * arguments instead. When execPath is specified, the execPath will be executed for - * relaunch instead of current app. Note that this method does not quit the app - * when executed, you have to call app.quit or app.exit after calling app.relaunch - * to make the app restart. When app.relaunch is called for multiple times, - * multiple instances will be started after current instance exited. An example of - * restarting current instance immediately and adding a new command line argument - * to the new instance: - */ - relaunch(options?: RelaunchOptions): void; - /** - * Releases all locks that were created by requestSingleInstanceLock. This will - * allow multiple instances of the application to once again run side by side. - */ - releaseSingleInstanceLock(): void; - /** - * This method checks if the current executable as the default handler for a - * protocol (aka URI scheme). If so, it will remove the app as the default handler. - */ - removeAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; - /** - * The return value of this method indicates whether or not this instance of your - * application successfully obtained the lock. If it failed to obtain the lock, - * you can assume that another instance of your application is already running with - * the lock and exit immediately. I.e. This method returns true if your process is - * the primary instance of your application and your app should continue loading. - * It returns false if your process should immediately quit as it has sent its - * parameters to another instance that has already acquired the lock. On macOS, the - * system enforces single instance automatically when users try to open a second - * instance of your app in Finder, and the open-file and open-url events will be - * emitted for that. However when users start your app in command line, the - * system's single instance mechanism will be bypassed, and you have to use this - * method to ensure single instance. An example of activating the window of primary - * instance when a second instance starts: - */ - requestSingleInstanceLock(): boolean; - /** - * Set the about panel options. This will override the values defined in the app's - * .plist file on MacOS. See the Apple docs for more details. On Linux, values must - * be set in order to be shown; there are no defaults. - */ - setAboutPanelOptions(options: AboutPanelOptionsOptions): void; - /** - * Manually enables Chrome's accessibility support, allowing to expose - * accessibility switch to users in application settings. See Chromium's - * accessibility docs for more details. Disabled by default. This API must be - * called after the ready event is emitted. Note: Rendering accessibility tree can - * significantly affect the performance of your app. It should not be enabled by - * default. Deprecated Soon - */ - setAccessibilitySupportEnabled(enabled: boolean): void; - /** - * Sets or creates a directory your app's logs which can then be manipulated with - * app.getPath() or app.setPath(pathName, newPath). Calling app.setAppLogsPath() - * without a path parameter will result in this directory being set to - * /Library/Logs/YourAppName on macOS, and inside the userData directory on Linux - * and Windows. - */ - setAppLogsPath(path?: string): void; - /** - * Changes the Application User Model ID to id. - */ - setAppUserModelId(id: string): void; - /** - * This method sets the current executable as the default handler for a protocol - * (aka URI scheme). It allows you to integrate your app deeper into the operating - * system. Once registered, all links with your-protocol:// will be opened with the - * current executable. The whole link, including protocol, will be passed to your - * application as a parameter. On Windows, you can provide optional parameters - * path, the path to your executable, and args, an array of arguments to be passed - * to your executable when it launches. Note: On macOS, you can only register - * protocols that have been added to your app's info.plist, which can not be - * modified at runtime. You can however change the file with a simple text editor - * or script during build time. Please refer to Apple's documentation for details. - * Note: In a Windows Store environment (when packaged as an appx) this API will - * return true for all calls but the registry key it sets won't be accessible by - * other applications. In order to register your Windows Store application as a - * default protocol handler you must declare the protocol in your manifest. The API - * uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. - */ - setAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; - /** - * Sets the counter badge for current app. Setting the count to 0 will hide the - * badge. On macOS, it shows on the dock icon. On Linux, it only works for Unity - * launcher. Note: Unity launcher requires the existence of a .desktop file to - * work, for more information please read Desktop Environment Integration. - */ - setBadgeCount(count: number): boolean; - /** - * Sets or removes a custom Jump List for the application, and returns one of the - * following strings: If categories is null the previously set custom Jump List (if - * any) will be replaced by the standard Jump List for the app (managed by - * Windows). Note: If a JumpListCategory object has neither the type nor the name - * property set then its type is assumed to be tasks. If the name property is set - * but the type property is omitted then the type is assumed to be custom. Note: - * Users can remove items from custom categories, and Windows will not allow a - * removed item to be added back into a custom category until after the next - * successful call to app.setJumpList(categories). Any attempt to re-add a removed - * item to a custom category earlier than that will result in the entire custom - * category being omitted from the Jump List. The list of removed items can be - * obtained using app.getJumpListSettings(). Here's a very simple example of - * creating a custom Jump List: - */ - setJumpList(categories: JumpListCategory[]): void; - /** - * Set the app's login item settings. To work with Electron's autoUpdater on - * Windows, which uses Squirrel, you'll want to set the launch path to Update.exe, - * and pass arguments that specify your application name. For example: - */ - setLoginItemSettings(settings: Settings): void; - /** - * Overrides the current application's name. - */ - setName(name: string): void; - /** - * Overrides the path to a special directory or file associated with name. If the - * path specifies a directory that does not exist, an Error is thrown. In that - * case, the directory should be created with fs.mkdirSync or similar. You can only - * override paths of a name defined in app.getPath. By default, web pages' cookies - * and caches will be stored under the userData directory. If you want to change - * this location, you have to override the userData path before the ready event of - * the app module is emitted. - */ - setPath(name: string, path: string): void; - /** - * Creates an NSUserActivity and sets it as the current activity. The activity is - * eligible for Handoff to another device afterward. - */ - setUserActivity(type: string, userInfo: any, webpageURL?: string): void; - /** - * Adds tasks to the Tasks category of the JumpList on Windows. tasks is an array - * of Task objects. Note: If you'd like to customize the Jump List even more use - * app.setJumpList(categories) instead. - */ - setUserTasks(tasks: Task[]): boolean; - /** - * Shows application windows after they were hidden. Does not automatically focus - * them. - */ - show(): void; - /** - * Show the app's about panel options. These options can be overridden with - * app.setAboutPanelOptions(options). - */ - showAboutPanel(): void; - /** - * Show the platform's native emoji picker. - */ - showEmojiPanel(): void; - /** - * Start accessing a security scoped resource. With this method Electron - * applications that are packaged for the Mac App Store may reach outside their - * sandbox to access files chosen by the user. See Apple's documentation for a - * description of how this system works. - */ - startAccessingSecurityScopedResource(bookmarkData: string): Function; - /** - * Updates the current activity if its type matches type, merging the entries from - * userInfo into its current userInfo dictionary. - */ - updateCurrentActivity(type: string, userInfo: any): void; - whenReady(): Promise; - /** - * A Boolean property that's true if Chrome's accessibility support is enabled, - * false otherwise. This property will be true if the use of assistive - * technologies, such as screen readers, has been detected. Setting this property - * to true manually enables Chrome's accessibility support, allowing developers to - * expose accessibility switch to users in application settings. See Chromium's - * accessibility docs for more details. Disabled by default. This API must be - * called after the ready event is emitted. Note: Rendering accessibility tree can - * significantly affect the performance of your app. It should not be enabled by - * default. - */ - accessibilitySupportEnabled?: boolean; - /** - * A Boolean which when true disables the overrides that Electron has in place to - * ensure renderer processes are restarted on every navigation. The current - * default value for this property is false. The intention is for these overrides - * to become disabled by default and then at some point in the future this property - * will be removed. This property impacts which native modules you can use in the - * renderer process. For more information on the direction Electron is going with - * renderer process restarts and usage of native modules in the renderer process - * please check out this Tracking Issue. - */ - allowRendererProcessReuse?: boolean; - /** - * A Menu property that return Menu if one has been set and null otherwise. Users - * can pass a Menu to set this property. - */ - applicationMenu?: Menu; - commandLine: CommandLine; - dock: Dock; - /** - * A Boolean property that returns true if the app is packaged, false otherwise. - * For many apps, this property can be used to distinguish development and - * production environments. - */ - isPackaged?: boolean; - /** - * A String which is the user agent string Electron will use as a global fallback. - * This is the user agent that will be used when no user agent is set at the - * webContents or session level. Useful for ensuring your entire app has the same - * user agent. Set to a custom value as early as possible in your apps - * initialization to ensure that your overridden value is used. - */ - userAgentFallback?: string; - } - - interface AutoUpdater extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/auto-updater - - /** - * This event is emitted after a user calls quitAndInstall(). When this API is - * called, the before-quit event is not emitted before all windows are closed. As a - * result you should listen to this event if you wish to perform actions before the - * windows are closed while a process is quitting, as well as listening to - * before-quit. - */ - on(event: 'before-quit-for-update', listener: Function): this; - once(event: 'before-quit-for-update', listener: Function): this; - addListener(event: 'before-quit-for-update', listener: Function): this; - removeListener(event: 'before-quit-for-update', listener: Function): this; - /** - * Emitted when checking if an update has started. - */ - on(event: 'checking-for-update', listener: Function): this; - once(event: 'checking-for-update', listener: Function): this; - addListener(event: 'checking-for-update', listener: Function): this; - removeListener(event: 'checking-for-update', listener: Function): this; - /** - * Emitted when there is an error while updating. - */ - on(event: 'error', listener: (error: Error) => void): this; - once(event: 'error', listener: (error: Error) => void): this; - addListener(event: 'error', listener: (error: Error) => void): this; - removeListener(event: 'error', listener: (error: Error) => void): this; - /** - * Emitted when there is an available update. The update is downloaded - * automatically. - */ - on(event: 'update-available', listener: Function): this; - once(event: 'update-available', listener: Function): this; - addListener(event: 'update-available', listener: Function): this; - removeListener(event: 'update-available', listener: Function): this; - /** - * Emitted when an update has been downloaded. On Windows only releaseName is - * available. Note: It is not strictly necessary to handle this event. A - * successfully downloaded update will still be applied the next time the - * application starts. - */ - on(event: 'update-downloaded', listener: (event: Event, - releaseNotes: string, - releaseName: string, - releaseDate: Date, - updateURL: string) => void): this; - once(event: 'update-downloaded', listener: (event: Event, - releaseNotes: string, - releaseName: string, - releaseDate: Date, - updateURL: string) => void): this; - addListener(event: 'update-downloaded', listener: (event: Event, - releaseNotes: string, - releaseName: string, - releaseDate: Date, - updateURL: string) => void): this; - removeListener(event: 'update-downloaded', listener: (event: Event, - releaseNotes: string, - releaseName: string, - releaseDate: Date, - updateURL: string) => void): this; - /** - * Emitted when there is no available update. - */ - on(event: 'update-not-available', listener: Function): this; - once(event: 'update-not-available', listener: Function): this; - addListener(event: 'update-not-available', listener: Function): this; - removeListener(event: 'update-not-available', listener: Function): this; - /** - * Asks the server whether there is an update. You must call setFeedURL before - * using this API. - */ - checkForUpdates(): void; - getFeedURL(): string; - /** - * Restarts the app and installs the update after it has been downloaded. It should - * only be called after update-downloaded has been emitted. Under the hood calling - * autoUpdater.quitAndInstall() will close all application windows first, and - * automatically call app.quit() after all windows have been closed. Note: It is - * not strictly necessary to call this function to apply an update, as a - * successfully downloaded update will always be applied the next time the - * application starts. - */ - quitAndInstall(): void; - /** - * Sets the url and initialize the auto updater. - */ - setFeedURL(options: FeedURLOptions): void; - } - - interface BluetoothDevice { - - // Docs: http://electronjs.org/docs/api/structures/bluetooth-device - - deviceId: string; - deviceName: string; - } - - class BrowserView extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/browser-view - - constructor(options?: BrowserViewConstructorOptions); - static fromId(id: number): BrowserView; - static fromWebContents(webContents: WebContents): (BrowserView) | (null); - static getAllViews(): BrowserView[]; - /** - * Force closing the view, the unload and beforeunload events won't be emitted for - * the web page. After you're done with a view, call this function in order to free - * memory and other resources as soon as possible. - */ - destroy(): void; - isDestroyed(): boolean; - setAutoResize(options: AutoResizeOptions): void; - setBackgroundColor(color: string): void; - /** - * Resizes and moves the view to the supplied bounds relative to the window. - */ - setBounds(bounds: Rectangle): void; - id: number; - webContents: WebContents; - } - - class BrowserWindow extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/browser-window - - /** - * Emitted when the window is set or unset to show always on top of other windows. - */ - on(event: 'always-on-top-changed', listener: (event: Event, - isAlwaysOnTop: boolean) => void): this; - once(event: 'always-on-top-changed', listener: (event: Event, - isAlwaysOnTop: boolean) => void): this; - addListener(event: 'always-on-top-changed', listener: (event: Event, - isAlwaysOnTop: boolean) => void): this; - removeListener(event: 'always-on-top-changed', listener: (event: Event, - isAlwaysOnTop: boolean) => void): this; - /** - * Emitted when an App Command is invoked. These are typically related to keyboard - * media keys or browser commands, as well as the "Back" button built into some - * mice on Windows. Commands are lowercased, underscores are replaced with hyphens, - * and the APPCOMMAND_ prefix is stripped off. e.g. APPCOMMAND_BROWSER_BACKWARD is - * emitted as browser-backward. The following app commands are explictly supported - * on Linux: - */ - on(event: 'app-command', listener: (event: Event, - command: string) => void): this; - once(event: 'app-command', listener: (event: Event, - command: string) => void): this; - addListener(event: 'app-command', listener: (event: Event, - command: string) => void): this; - removeListener(event: 'app-command', listener: (event: Event, - command: string) => void): this; - /** - * Emitted when the window loses focus. - */ - on(event: 'blur', listener: Function): this; - once(event: 'blur', listener: Function): this; - addListener(event: 'blur', listener: Function): this; - removeListener(event: 'blur', listener: Function): this; - /** - * Emitted when the window is going to be closed. It's emitted before the - * beforeunload and unload event of the DOM. Calling event.preventDefault() will - * cancel the close. Usually you would want to use the beforeunload handler to - * decide whether the window should be closed, which will also be called when the - * window is reloaded. In Electron, returning any value other than undefined would - * cancel the close. For example: Note: There is a subtle difference between the - * behaviors of window.onbeforeunload = handler and - * window.addEventListener('beforeunload', handler). It is recommended to always - * set the event.returnValue explicitly, instead of only returning a value, as the - * former works more consistently within Electron. - */ - on(event: 'close', listener: (event: Event) => void): this; - once(event: 'close', listener: (event: Event) => void): this; - addListener(event: 'close', listener: (event: Event) => void): this; - removeListener(event: 'close', listener: (event: Event) => void): this; - /** - * Emitted when the window is closed. After you have received this event you should - * remove the reference to the window and avoid using it any more. - */ - on(event: 'closed', listener: Function): this; - once(event: 'closed', listener: Function): this; - addListener(event: 'closed', listener: Function): this; - removeListener(event: 'closed', listener: Function): this; - /** - * Emitted when the window enters a full-screen state. - */ - on(event: 'enter-full-screen', listener: Function): this; - once(event: 'enter-full-screen', listener: Function): this; - addListener(event: 'enter-full-screen', listener: Function): this; - removeListener(event: 'enter-full-screen', listener: Function): this; - /** - * Emitted when the window enters a full-screen state triggered by HTML API. - */ - on(event: 'enter-html-full-screen', listener: Function): this; - once(event: 'enter-html-full-screen', listener: Function): this; - addListener(event: 'enter-html-full-screen', listener: Function): this; - removeListener(event: 'enter-html-full-screen', listener: Function): this; - /** - * Emitted when the window gains focus. - */ - on(event: 'focus', listener: Function): this; - once(event: 'focus', listener: Function): this; - addListener(event: 'focus', listener: Function): this; - removeListener(event: 'focus', listener: Function): this; - /** - * Emitted when the window is hidden. - */ - on(event: 'hide', listener: Function): this; - once(event: 'hide', listener: Function): this; - addListener(event: 'hide', listener: Function): this; - removeListener(event: 'hide', listener: Function): this; - /** - * Emitted when the window leaves a full-screen state. - */ - on(event: 'leave-full-screen', listener: Function): this; - once(event: 'leave-full-screen', listener: Function): this; - addListener(event: 'leave-full-screen', listener: Function): this; - removeListener(event: 'leave-full-screen', listener: Function): this; - /** - * Emitted when the window leaves a full-screen state triggered by HTML API. - */ - on(event: 'leave-html-full-screen', listener: Function): this; - once(event: 'leave-html-full-screen', listener: Function): this; - addListener(event: 'leave-html-full-screen', listener: Function): this; - removeListener(event: 'leave-html-full-screen', listener: Function): this; - /** - * Emitted when window is maximized. - */ - on(event: 'maximize', listener: Function): this; - once(event: 'maximize', listener: Function): this; - addListener(event: 'maximize', listener: Function): this; - removeListener(event: 'maximize', listener: Function): this; - /** - * Emitted when the window is minimized. - */ - on(event: 'minimize', listener: Function): this; - once(event: 'minimize', listener: Function): this; - addListener(event: 'minimize', listener: Function): this; - removeListener(event: 'minimize', listener: Function): this; - /** - * Emitted when the window is being moved to a new position. Note: On macOS this - * event is an alias of moved. - */ - on(event: 'move', listener: Function): this; - once(event: 'move', listener: Function): this; - addListener(event: 'move', listener: Function): this; - removeListener(event: 'move', listener: Function): this; - /** - * Emitted once when the window is moved to a new position. - */ - on(event: 'moved', listener: Function): this; - once(event: 'moved', listener: Function): this; - addListener(event: 'moved', listener: Function): this; - removeListener(event: 'moved', listener: Function): this; - /** - * Emitted when the native new tab button is clicked. - */ - on(event: 'new-window-for-tab', listener: Function): this; - once(event: 'new-window-for-tab', listener: Function): this; - addListener(event: 'new-window-for-tab', listener: Function): this; - removeListener(event: 'new-window-for-tab', listener: Function): this; - /** - * Emitted when the document changed its title, calling event.preventDefault() will - * prevent the native window's title from changing. explicitSet is false when title - * is synthesized from file url. - */ - on(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - once(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - addListener(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - removeListener(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - /** - * Emitted when the web page has been rendered (while not being shown) and window - * can be displayed without a visual flash. - */ - on(event: 'ready-to-show', listener: Function): this; - once(event: 'ready-to-show', listener: Function): this; - addListener(event: 'ready-to-show', listener: Function): this; - removeListener(event: 'ready-to-show', listener: Function): this; - /** - * Emitted after the window has been resized. - */ - on(event: 'resize', listener: Function): this; - once(event: 'resize', listener: Function): this; - addListener(event: 'resize', listener: Function): this; - removeListener(event: 'resize', listener: Function): this; - /** - * Emitted when the unresponsive web page becomes responsive again. - */ - on(event: 'responsive', listener: Function): this; - once(event: 'responsive', listener: Function): this; - addListener(event: 'responsive', listener: Function): this; - removeListener(event: 'responsive', listener: Function): this; - /** - * Emitted when the window is restored from a minimized state. - */ - on(event: 'restore', listener: Function): this; - once(event: 'restore', listener: Function): this; - addListener(event: 'restore', listener: Function): this; - removeListener(event: 'restore', listener: Function): this; - /** - * Emitted when scroll wheel event phase has begun. - */ - on(event: 'scroll-touch-begin', listener: Function): this; - once(event: 'scroll-touch-begin', listener: Function): this; - addListener(event: 'scroll-touch-begin', listener: Function): this; - removeListener(event: 'scroll-touch-begin', listener: Function): this; - /** - * Emitted when scroll wheel event phase filed upon reaching the edge of element. - */ - on(event: 'scroll-touch-edge', listener: Function): this; - once(event: 'scroll-touch-edge', listener: Function): this; - addListener(event: 'scroll-touch-edge', listener: Function): this; - removeListener(event: 'scroll-touch-edge', listener: Function): this; - /** - * Emitted when scroll wheel event phase has ended. - */ - on(event: 'scroll-touch-end', listener: Function): this; - once(event: 'scroll-touch-end', listener: Function): this; - addListener(event: 'scroll-touch-end', listener: Function): this; - removeListener(event: 'scroll-touch-end', listener: Function): this; - /** - * Emitted when window session is going to end due to force shutdown or machine - * restart or session log off. - */ - on(event: 'session-end', listener: Function): this; - once(event: 'session-end', listener: Function): this; - addListener(event: 'session-end', listener: Function): this; - removeListener(event: 'session-end', listener: Function): this; - /** - * Emitted when the window opens a sheet. - */ - on(event: 'sheet-begin', listener: Function): this; - once(event: 'sheet-begin', listener: Function): this; - addListener(event: 'sheet-begin', listener: Function): this; - removeListener(event: 'sheet-begin', listener: Function): this; - /** - * Emitted when the window has closed a sheet. - */ - on(event: 'sheet-end', listener: Function): this; - once(event: 'sheet-end', listener: Function): this; - addListener(event: 'sheet-end', listener: Function): this; - removeListener(event: 'sheet-end', listener: Function): this; - /** - * Emitted when the window is shown. - */ - on(event: 'show', listener: Function): this; - once(event: 'show', listener: Function): this; - addListener(event: 'show', listener: Function): this; - removeListener(event: 'show', listener: Function): this; - /** - * Emitted on 3-finger swipe. Possible directions are up, right, down, left. - */ - on(event: 'swipe', listener: (event: Event, - direction: string) => void): this; - once(event: 'swipe', listener: (event: Event, - direction: string) => void): this; - addListener(event: 'swipe', listener: (event: Event, - direction: string) => void): this; - removeListener(event: 'swipe', listener: (event: Event, - direction: string) => void): this; - /** - * Emitted when the window exits from a maximized state. - */ - on(event: 'unmaximize', listener: Function): this; - once(event: 'unmaximize', listener: Function): this; - addListener(event: 'unmaximize', listener: Function): this; - removeListener(event: 'unmaximize', listener: Function): this; - /** - * Emitted when the web page becomes unresponsive. - */ - on(event: 'unresponsive', listener: Function): this; - once(event: 'unresponsive', listener: Function): this; - addListener(event: 'unresponsive', listener: Function): this; - removeListener(event: 'unresponsive', listener: Function): this; - /** - * Emitted before the window is moved. Calling event.preventDefault() will prevent - * the window from being moved. Note that this is only emitted when the window is - * being resized manually. Resizing the window with setBounds/setSize will not emit - * this event. - */ - on(event: 'will-move', listener: (event: Event, - /** - * ` Location the window is being moved to. - */ - newBounds: Rectangle) => void): this; - once(event: 'will-move', listener: (event: Event, - /** - * ` Location the window is being moved to. - */ - newBounds: Rectangle) => void): this; - addListener(event: 'will-move', listener: (event: Event, - /** - * ` Location the window is being moved to. - */ - newBounds: Rectangle) => void): this; - removeListener(event: 'will-move', listener: (event: Event, - /** - * ` Location the window is being moved to. - */ - newBounds: Rectangle) => void): this; - /** - * Emitted before the window is resized. Calling event.preventDefault() will - * prevent the window from being resized. Note that this is only emitted when the - * window is being resized manually. Resizing the window with setBounds/setSize - * will not emit this event. - */ - on(event: 'will-resize', listener: (event: Event, - /** - * ` Size the window is being resized to. - */ - newBounds: Rectangle) => void): this; - once(event: 'will-resize', listener: (event: Event, - /** - * ` Size the window is being resized to. - */ - newBounds: Rectangle) => void): this; - addListener(event: 'will-resize', listener: (event: Event, - /** - * ` Size the window is being resized to. - */ - newBounds: Rectangle) => void): this; - removeListener(event: 'will-resize', listener: (event: Event, - /** - * ` Size the window is being resized to. - */ - newBounds: Rectangle) => void): this; - constructor(options?: BrowserWindowConstructorOptions); - /** - * Adds DevTools extension located at path, and returns extension's name. The - * extension will be remembered so you only need to call this API once, this API is - * not for programming use. If you try to add an extension that has already been - * loaded, this method will not return and instead log a warning to the console. - * The method will also not return if the extension's manifest is missing or - * incomplete. Note: This API cannot be called before the ready event of the app - * module is emitted. - */ - static addDevToolsExtension(path: string): void; - /** - * Adds Chrome extension located at path, and returns extension's name. The method - * will also not return if the extension's manifest is missing or incomplete. Note: - * This API cannot be called before the ready event of the app module is emitted. - */ - static addExtension(path: string): void; - static fromBrowserView(browserView: BrowserView): (BrowserWindow) | (null); - static fromId(id: number): BrowserWindow; - static fromWebContents(webContents: WebContents): BrowserWindow; - static getAllWindows(): BrowserWindow[]; - /** - * To check if a DevTools extension is installed you can run the following: Note: - * This API cannot be called before the ready event of the app module is emitted. - */ - static getDevToolsExtensions(): DevToolsExtensions; - /** - * Note: This API cannot be called before the ready event of the app module is - * emitted. - */ - static getExtensions(): Extensions; - static getFocusedWindow(): (BrowserWindow) | (null); - /** - * Remove a DevTools extension by name. Note: This API cannot be called before the - * ready event of the app module is emitted. - */ - static removeDevToolsExtension(name: string): void; - /** - * Remove a Chrome extension by name. Note: This API cannot be called before the - * ready event of the app module is emitted. - */ - static removeExtension(name: string): void; - /** - * Replacement API for setBrowserView supporting work with multi browser views. - */ - addBrowserView(browserView: BrowserView): void; - /** - * Adds a window as a tab on this window, after the tab for the window instance. - */ - addTabbedWindow(browserWindow: BrowserWindow): void; - /** - * Removes focus from the window. - */ - blur(): void; - blurWebView(): void; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(callback: (image: NativeImage) => void): void; - /** - * Captures a snapshot of the page within rect. Omitting rect will capture the - * whole visible page. - */ - capturePage(rect?: Rectangle): Promise; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; - /** - * Moves window to the center of the screen. - */ - center(): void; - /** - * Try to close the window. This has the same effect as a user manually clicking - * the close button of the window. The web page may cancel the close though. See - * the close event. - */ - close(): void; - /** - * Closes the currently open Quick Look panel. - */ - closeFilePreview(): void; - /** - * Force closing the window, the unload and beforeunload event won't be emitted for - * the web page, and close event will also not be emitted for this window, but it - * guarantees the closed event will be emitted. - */ - destroy(): void; - /** - * Starts or stops flashing the window to attract user's attention. - */ - flashFrame(flag: boolean): void; - /** - * Focuses on the window. - */ - focus(): void; - focusOnWebView(): void; - getBounds(): Rectangle; - getBrowserView(): (BrowserView) | (null); - /** - * Returns array of BrowserView what was an attached with addBrowserView or - * setBrowserView. Note: The BrowserView API is currently experimental and may - * change or be removed in future Electron releases. - */ - getBrowserViews(): void; - getChildWindows(): BrowserWindow[]; - getContentBounds(): Rectangle; - getContentSize(): number[]; - getMaximumSize(): number[]; - getMinimumSize(): number[]; - /** - * The native type of the handle is HWND on Windows, NSView* on macOS, and Window - * (unsigned long) on Linux. - */ - getNativeWindowHandle(): Buffer; - /** - * Note: whatever the current state of the window : maximized, minimized or in - * fullscreen, this function always returns the position and size of the window in - * normal state. In normal state, getBounds and getNormalBounds returns the same - * Rectangle. - */ - getNormalBounds(): Rectangle; - getOpacity(): number; - getParentWindow(): BrowserWindow; - getPosition(): number[]; - getRepresentedFilename(): string; - getSize(): number[]; - /** - * Note: The title of the web page can be different from the title of the native - * window. - */ - getTitle(): string; - hasShadow(): boolean; - /** - * Hides the window. - */ - hide(): void; - /** - * Hooks a windows message. The callback is called when the message is received in - * the WndProc. - */ - hookWindowMessage(message: number, callback: Function): void; - isAlwaysOnTop(): boolean; - /** - * On Linux always returns true. - */ - isClosable(): boolean; - isDestroyed(): boolean; - isDocumentEdited(): boolean; - isFocused(): boolean; - isFullScreen(): boolean; - isFullScreenable(): boolean; - isKiosk(): boolean; - /** - * On Linux always returns true. - */ - isMaximizable(): boolean; - isMaximized(): boolean; - isMenuBarAutoHide(): boolean; - isMenuBarVisible(): boolean; - /** - * On Linux always returns true. - */ - isMinimizable(): boolean; - isMinimized(): boolean; - isModal(): boolean; - /** - * On Linux always returns true. - */ - isMovable(): boolean; - isNormal(): boolean; - isResizable(): boolean; - isSimpleFullScreen(): boolean; - isVisible(): boolean; - /** - * Note: This API always returns false on Windows. - */ - isVisibleOnAllWorkspaces(): boolean; - isWindowMessageHooked(message: number): boolean; - /** - * Same as webContents.loadFile, filePath should be a path to an HTML file relative - * to the root of your application. See the webContents docs for more information. - */ - loadFile(filePath: string, options?: LoadFileOptions): Promise; - /** - * Same as webContents.loadURL(url[, options]). The url can be a remote address - * (e.g. http://) or a path to a local HTML file using the file:// protocol. To - * ensure that file URLs are properly formatted, it is recommended to use Node's - * url.format method: You can load a URL using a POST request with URL-encoded data - * by doing the following: - */ - loadURL(url: string, options?: LoadURLOptions): Promise; - /** - * Maximizes the window. This will also show (but not focus) the window if it isn't - * being displayed already. - */ - maximize(): void; - /** - * Merges all windows into one window with multiple tabs when native tabs are - * enabled and there is more than one open window. - */ - mergeAllWindows(): void; - /** - * Minimizes the window. On some platforms the minimized window will be shown in - * the Dock. - */ - minimize(): void; - /** - * Moves the current tab into a new window if native tabs are enabled and there is - * more than one tab in the current window. - */ - moveTabToNewWindow(): void; - /** - * Moves window to top(z-order) regardless of focus - */ - moveTop(): void; - /** - * Uses Quick Look to preview a file at a given path. - */ - previewFile(path: string, displayName?: string): void; - /** - * Same as webContents.reload. - */ - reload(): void; - removeBrowserView(browserView: BrowserView): void; - /** - * Remove the window's menu bar. - */ - removeMenu(): void; - /** - * Restores the window from minimized state to its previous state. - */ - restore(): void; - /** - * Selects the next tab when native tabs are enabled and there are other tabs in - * the window. - */ - selectNextTab(): void; - /** - * Selects the previous tab when native tabs are enabled and there are other tabs - * in the window. - */ - selectPreviousTab(): void; - /** - * Sets whether the window should show always on top of other windows. After - * setting this, the window is still a normal window, not a toolbox window which - * can not be focused on. - */ - setAlwaysOnTop(flag: boolean, level?: 'normal' | 'floating' | 'torn-off-menu' | 'modal-panel' | 'main-menu' | 'status' | 'pop-up-menu' | 'screen-saver', relativeLevel?: number): void; - /** - * Sets the properties for the window's taskbar button. Note: relaunchCommand and - * relaunchDisplayName must always be set together. If one of those properties is - * not set, then neither will be used. - */ - setAppDetails(options: AppDetailsOptions): void; - /** - * This will make a window maintain an aspect ratio. The extra size allows a - * developer to have space, specified in pixels, not included within the aspect - * ratio calculations. This API already takes into account the difference between a - * window's size and its content size. Consider a normal window with an HD video - * player and associated controls. Perhaps there are 15 pixels of controls on the - * left edge, 25 pixels of controls on the right edge and 50 pixels of controls - * below the player. In order to maintain a 16:9 aspect ratio (standard aspect - * ratio for HD @1920x1080) within the player itself we would call this function - * with arguments of 16/9 and [ 40, 50 ]. The second argument doesn't care where - * the extra width and height are within the content view--only that they exist. - * Sum any extra width and height areas you have within the overall content view. - * Calling this function with a value of 0 will remove any previously set aspect - * ratios. - */ - setAspectRatio(aspectRatio: number, extraSize: Size): void; - /** - * Controls whether to hide cursor when typing. - */ - setAutoHideCursor(autoHide: boolean): void; - /** - * Sets whether the window menu bar should hide itself automatically. Once set the - * menu bar will only show when users press the single Alt key. If the menu bar is - * already visible, calling setAutoHideMenuBar(true) won't hide it immediately. - */ - setAutoHideMenuBar(hide: boolean): void; - /** - * Sets the background color of the window. See Setting backgroundColor. - */ - setBackgroundColor(backgroundColor: string): void; - /** - * Resizes and moves the window to the supplied bounds. Any properties that are not - * supplied will default to their current values. - */ - setBounds(bounds: Rectangle, animate?: boolean): void; - setBrowserView(browserView: BrowserView): void; - /** - * Sets whether the window can be manually closed by user. On Linux does nothing. - */ - setClosable(closable: boolean): void; - /** - * Resizes and moves the window's client area (e.g. the web page) to the supplied - * bounds. - */ - setContentBounds(bounds: Rectangle, animate?: boolean): void; - /** - * Prevents the window contents from being captured by other apps. On macOS it sets - * the NSWindow's sharingType to NSWindowSharingNone. On Windows it calls - * SetWindowDisplayAffinity with WDA_MONITOR. - */ - setContentProtection(enable: boolean): void; - /** - * Resizes the window's client area (e.g. the web page) to width and height. - */ - setContentSize(width: number, height: number, animate?: boolean): void; - /** - * Specifies whether the window’s document has been edited, and the icon in title - * bar will become gray when set to true. - */ - setDocumentEdited(edited: boolean): void; - /** - * Disable or enable the window. - */ - setEnabled(enable: boolean): void; - /** - * Changes whether the window can be focused. - */ - setFocusable(focusable: boolean): void; - /** - * Sets whether the window should be in fullscreen mode. - */ - setFullScreen(flag: boolean): void; - /** - * Sets whether the maximize/zoom window button toggles fullscreen mode or - * maximizes the window. - */ - setFullScreenable(fullscreenable: boolean): void; - /** - * Sets whether the window should have a shadow. - */ - setHasShadow(hasShadow: boolean): void; - /** - * Changes window icon. - */ - setIcon(icon: NativeImage): void; - /** - * Makes the window ignore all mouse events. All mouse events happened in this - * window will be passed to the window below this window, but if this window has - * focus, it will still receive keyboard events. - */ - setIgnoreMouseEvents(ignore: boolean, options?: IgnoreMouseEventsOptions): void; - /** - * Enters or leaves the kiosk mode. - */ - setKiosk(flag: boolean): void; - /** - * Sets whether the window can be manually maximized by user. On Linux does - * nothing. - */ - setMaximizable(maximizable: boolean): void; - /** - * Sets the maximum size of window to width and height. - */ - setMaximumSize(width: number, height: number): void; - /** - * Sets the menu as the window's menu bar. - */ - setMenu(menu: (Menu) | (null)): void; - /** - * Sets whether the menu bar should be visible. If the menu bar is auto-hide, users - * can still bring up the menu bar by pressing the single Alt key. - */ - setMenuBarVisibility(visible: boolean): void; - /** - * Sets whether the window can be manually minimized by user. On Linux does - * nothing. - */ - setMinimizable(minimizable: boolean): void; - /** - * Sets the minimum size of window to width and height. - */ - setMinimumSize(width: number, height: number): void; - /** - * Sets whether the window can be moved by user. On Linux does nothing. - */ - setMovable(movable: boolean): void; - /** - * Sets the opacity of the window. On Linux does nothing. - */ - setOpacity(opacity: number): void; - /** - * Sets a 16 x 16 pixel overlay onto the current taskbar icon, usually used to - * convey some sort of application status or to passively notify the user. - */ - setOverlayIcon(overlay: (NativeImage) | (null), description: string): void; - /** - * Sets parent as current window's parent window, passing null will turn current - * window into a top-level window. - */ - setParentWindow(parent: BrowserWindow): void; - /** - * Moves window to x and y. - */ - setPosition(x: number, y: number, animate?: boolean): void; - /** - * Sets progress value in progress bar. Valid range is [0, 1.0]. Remove progress - * bar when progress < 0; Change to indeterminate mode when progress > 1. On Linux - * platform, only supports Unity desktop environment, you need to specify the - * *.desktop file name to desktopName field in package.json. By default, it will - * assume app.getName().desktop. On Windows, a mode can be passed. Accepted values - * are none, normal, indeterminate, error, and paused. If you call setProgressBar - * without a mode set (but with a value within the valid range), normal will be - * assumed. - */ - setProgressBar(progress: number, options?: ProgressBarOptions): void; - /** - * Sets the pathname of the file the window represents, and the icon of the file - * will show in window's title bar. - */ - setRepresentedFilename(filename: string): void; - /** - * Sets whether the window can be manually resized by user. - */ - setResizable(resizable: boolean): void; - /** - * Setting a window shape determines the area within the window where the system - * permits drawing and user interaction. Outside of the given region, no pixels - * will be drawn and no mouse events will be registered. Mouse events outside of - * the region will not be received by that window, but will fall through to - * whatever is behind the window. - */ - setShape(rects: Rectangle[]): void; - /** - * Changes the attachment point for sheets on macOS. By default, sheets are - * attached just below the window frame, but you may want to display them beneath a - * HTML-rendered toolbar. For example: - */ - setSheetOffset(offsetY: number, offsetX?: number): void; - /** - * Enters or leaves simple fullscreen mode. Simple fullscreen mode emulates the - * native fullscreen behavior found in versions of Mac OS X prior to Lion (10.7). - */ - setSimpleFullScreen(flag: boolean): void; - /** - * Resizes the window to width and height. If width or height are below any set - * minimum size constraints the window will snap to its minimum size. - */ - setSize(width: number, height: number, animate?: boolean): void; - /** - * Makes the window not show in the taskbar. - */ - setSkipTaskbar(skip: boolean): void; - /** - * Add a thumbnail toolbar with a specified set of buttons to the thumbnail image - * of a window in a taskbar button layout. Returns a Boolean object indicates - * whether the thumbnail has been added successfully. The number of buttons in - * thumbnail toolbar should be no greater than 7 due to the limited room. Once you - * setup the thumbnail toolbar, the toolbar cannot be removed due to the platform's - * limitation. But you can call the API with an empty array to clean the buttons. - * The buttons is an array of Button objects: The flags is an array that can - * include following Strings: - */ - setThumbarButtons(buttons: ThumbarButton[]): boolean; - /** - * Sets the region of the window to show as the thumbnail image displayed when - * hovering over the window in the taskbar. You can reset the thumbnail to be the - * entire window by specifying an empty region: { x: 0, y: 0, width: 0, height: 0 - * }. - */ - setThumbnailClip(region: Rectangle): void; - /** - * Sets the toolTip that is displayed when hovering over the window thumbnail in - * the taskbar. - */ - setThumbnailToolTip(toolTip: string): void; - /** - * Changes the title of native window to title. - */ - setTitle(title: string): void; - /** - * Sets the touchBar layout for the current window. Specifying null or undefined - * clears the touch bar. This method only has an effect if the machine has a touch - * bar and is running on macOS 10.12.1+. Note: The TouchBar API is currently - * experimental and may change or be removed in future Electron releases. - */ - setTouchBar(touchBar: TouchBar): void; - /** - * Adds a vibrancy effect to the browser window. Passing null or an empty string - * will remove the vibrancy effect on the window. - */ - setVibrancy(type: 'appearance-based' | 'light' | 'dark' | 'titlebar' | 'selection' | 'menu' | 'popover' | 'sidebar' | 'medium-light' | 'ultra-dark'): void; - /** - * Sets whether the window should be visible on all workspaces. Note: This API does - * nothing on Windows. - */ - setVisibleOnAllWorkspaces(visible: boolean, options?: VisibleOnAllWorkspacesOptions): void; - /** - * Sets whether the window traffic light buttons should be visible. This cannot be - * called when titleBarStyle is set to customButtonsOnHover. - */ - setWindowButtonVisibility(visible: boolean): void; - /** - * Shows and gives focus to the window. - */ - show(): void; - /** - * Same as webContents.showDefinitionForSelection(). - */ - showDefinitionForSelection(): void; - /** - * Shows the window but doesn't focus on it. - */ - showInactive(): void; - /** - * Toggles the visibility of the tab bar if native tabs are enabled and there is - * only one tab in the current window. - */ - toggleTabBar(): void; - /** - * Unhooks all of the window messages. - */ - unhookAllWindowMessages(): void; - /** - * Unhook the window message. - */ - unhookWindowMessage(message: number): void; - /** - * Unmaximizes the window. - */ - unmaximize(): void; - id: number; - webContents: WebContents; - } - - class BrowserWindowProxy extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/browser-window-proxy - - /** - * Removes focus from the child window. - */ - blur(): void; - /** - * Forcefully closes the child window without calling its unload event. - */ - close(): void; - /** - * Evaluates the code in the child window. - */ - eval(code: string): void; - /** - * Focuses the child window (brings the window to front). - */ - focus(): void; - /** - * Sends a message to the child window with the specified origin or * for no origin - * preference. In addition to these methods, the child window implements - * window.opener object with no properties and a single method. - */ - postMessage(message: string, targetOrigin: string): void; - /** - * Invokes the print dialog on the child window. - */ - print(): void; - closed: boolean; - } - - interface Certificate { - - // Docs: http://electronjs.org/docs/api/structures/certificate - - /** - * PEM encoded data - */ - data: string; - /** - * Fingerprint of the certificate - */ - fingerprint: string; - /** - * Issuer principal - */ - issuer: CertificatePrincipal; - /** - * Issuer certificate (if not self-signed) - */ - issuerCert: Certificate; - /** - * Issuer's Common Name - */ - issuerName: string; - /** - * Hex value represented string - */ - serialNumber: string; - /** - * Subject principal - */ - subject: CertificatePrincipal; - /** - * Subject's Common Name - */ - subjectName: string; - /** - * End date of the certificate being valid in seconds - */ - validExpiry: number; - /** - * Start date of the certificate being valid in seconds - */ - validStart: number; - } - - interface CertificatePrincipal { - - // Docs: http://electronjs.org/docs/api/structures/certificate-principal - - /** - * Common Name. - */ - commonName: string; - /** - * Country or region. - */ - country: string; - /** - * Locality. - */ - locality: string; - /** - * Organization names. - */ - organizations: string[]; - /** - * Organization Unit names. - */ - organizationUnits: string[]; - /** - * State or province. - */ - state: string; - } - - class ClientRequest extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/client-request - - /** - * Emitted when the request is aborted. The abort event will not be fired if the - * request is already closed. - */ - on(event: 'abort', listener: Function): this; - once(event: 'abort', listener: Function): this; - addListener(event: 'abort', listener: Function): this; - removeListener(event: 'abort', listener: Function): this; - /** - * Emitted as the last event in the HTTP request-response transaction. The close - * event indicates that no more events will be emitted on either the request or - * response objects. - */ - on(event: 'close', listener: Function): this; - once(event: 'close', listener: Function): this; - addListener(event: 'close', listener: Function): this; - removeListener(event: 'close', listener: Function): this; - /** - * Emitted when the net module fails to issue a network request. Typically when the - * request object emits an error event, a close event will subsequently follow and - * no response object will be provided. - */ - on(event: 'error', listener: ( - /** - * an error object providing some information about the failure. - */ - error: Error) => void): this; - once(event: 'error', listener: ( - /** - * an error object providing some information about the failure. - */ - error: Error) => void): this; - addListener(event: 'error', listener: ( - /** - * an error object providing some information about the failure. - */ - error: Error) => void): this; - removeListener(event: 'error', listener: ( - /** - * an error object providing some information about the failure. - */ - error: Error) => void): this; - /** - * Emitted just after the last chunk of the request's data has been written into - * the request object. - */ - on(event: 'finish', listener: Function): this; - once(event: 'finish', listener: Function): this; - addListener(event: 'finish', listener: Function): this; - removeListener(event: 'finish', listener: Function): this; - /** - * Emitted when an authenticating proxy is asking for user credentials. The - * callback function is expected to be called back with user credentials: Providing - * empty credentials will cancel the request and report an authentication error on - * the response object: - */ - on(event: 'login', listener: (authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - once(event: 'login', listener: (authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - addListener(event: 'login', listener: (authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - removeListener(event: 'login', listener: (authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - /** - * Emitted when there is redirection and the mode is manual. Calling - * request.followRedirect will continue with the redirection. - */ - on(event: 'redirect', listener: (statusCode: number, - method: string, - redirectUrl: string, - responseHeaders: any) => void): this; - once(event: 'redirect', listener: (statusCode: number, - method: string, - redirectUrl: string, - responseHeaders: any) => void): this; - addListener(event: 'redirect', listener: (statusCode: number, - method: string, - redirectUrl: string, - responseHeaders: any) => void): this; - removeListener(event: 'redirect', listener: (statusCode: number, - method: string, - redirectUrl: string, - responseHeaders: any) => void): this; - on(event: 'response', listener: ( - /** - * An object representing the HTTP response message. - */ - response: IncomingMessage) => void): this; - once(event: 'response', listener: ( - /** - * An object representing the HTTP response message. - */ - response: IncomingMessage) => void): this; - addListener(event: 'response', listener: ( - /** - * An object representing the HTTP response message. - */ - response: IncomingMessage) => void): this; - removeListener(event: 'response', listener: ( - /** - * An object representing the HTTP response message. - */ - response: IncomingMessage) => void): this; - constructor(options: 'method' | 'url' | 'session' | 'partition' | 'protocol' | 'host' | 'hostname' | 'port' | 'path' | 'redirect'); - /** - * Cancels an ongoing HTTP transaction. If the request has already emitted the - * close event, the abort operation will have no effect. Otherwise an ongoing event - * will emit abort and close events. Additionally, if there is an ongoing response - * object,it will emit the aborted event. - */ - abort(): void; - /** - * Sends the last chunk of the request data. Subsequent write or end operations - * will not be allowed. The finish event is emitted just after the end operation. - */ - end(chunk?: (string) | (Buffer), encoding?: string, callback?: Function): void; - /** - * Continues any deferred redirection request when the redirection mode is manual. - */ - followRedirect(): void; - getHeader(name: string): Header; - /** - * You can use this method in conjunction with POST requests to get the progress of - * a file upload or other data transfer. - */ - getUploadProgress(): UploadProgress; - /** - * Removes a previously set extra header name. This method can be called only - * before first write. Trying to call it after the first write will throw an error. - */ - removeHeader(name: string): void; - /** - * Adds an extra HTTP header. The header name will issued as it is without - * lowercasing. It can be called only before first write. Calling this method after - * the first write will throw an error. If the passed value is not a String, its - * toString() method will be called to obtain the final value. - */ - setHeader(name: string, value: any): void; - /** - * callback is essentially a dummy function introduced in the purpose of keeping - * similarity with the Node.js API. It is called asynchronously in the next tick - * after chunk content have been delivered to the Chromium networking layer. - * Contrary to the Node.js implementation, it is not guaranteed that chunk content - * have been flushed on the wire before callback is called. Adds a chunk of data to - * the request body. The first write operation may cause the request headers to be - * issued on the wire. After the first write operation, it is not allowed to add or - * remove a custom header. - */ - write(chunk: (string) | (Buffer), encoding?: string, callback?: Function): void; - chunkedEncoding: boolean; - } - - interface Clipboard extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/clipboard - - availableFormats(type?: 'selection' | 'clipboard'): string[]; - /** - * Clears the clipboard content. - */ - clear(type?: 'selection' | 'clipboard'): void; - has(format: string, type?: 'selection' | 'clipboard'): boolean; - read(format: string): string; - /** - * Returns an Object containing title and url keys representing the bookmark in the - * clipboard. The title and url values will be empty strings when the bookmark is - * unavailable. - */ - readBookmark(): ReadBookmark; - readBuffer(format: string): Buffer; - /** - * This method uses synchronous IPC when called from the renderer process. The - * cached value is reread from the find pasteboard whenever the application is - * activated. - */ - readFindText(): string; - readHTML(type?: 'selection' | 'clipboard'): string; - readImage(type?: 'selection' | 'clipboard'): NativeImage; - readRTF(type?: 'selection' | 'clipboard'): string; - readText(type?: 'selection' | 'clipboard'): string; - /** - * Writes data to the clipboard. - */ - write(data: Data, type?: 'selection' | 'clipboard'): void; - /** - * Writes the title and url into the clipboard as a bookmark. Note: Most apps on - * Windows don't support pasting bookmarks into them so you can use clipboard.write - * to write both a bookmark and fallback text to the clipboard. - */ - writeBookmark(title: string, url: string, type?: 'selection' | 'clipboard'): void; - /** - * Writes the buffer into the clipboard as format. - */ - writeBuffer(format: string, buffer: Buffer, type?: 'selection' | 'clipboard'): void; - /** - * Writes the text into the find pasteboard (the pasteboard that holds information - * about the current state of the active application’s find panel) as plain text. - * This method uses synchronous IPC when called from the renderer process. - */ - writeFindText(text: string): void; - /** - * Writes markup to the clipboard. - */ - writeHTML(markup: string, type?: 'selection' | 'clipboard'): void; - /** - * Writes image to the clipboard. - */ - writeImage(image: NativeImage, type?: 'selection' | 'clipboard'): void; - /** - * Writes the text into the clipboard in RTF. - */ - writeRTF(text: string, type?: 'selection' | 'clipboard'): void; - /** - * Writes the text into the clipboard as plain text. - */ - writeText(text: string, type?: 'selection' | 'clipboard'): void; - } - - interface ContentTracing extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/content-tracing - - /** - * Get a set of category groups. The category groups can change as new code paths - * are reached. Once all child processes have acknowledged the getCategories - * request the callback is invoked with an array of category groups. Deprecated - * Soon - */ - getCategories(callback: (categories: string[]) => void): void; - /** - * Get a set of category groups. The category groups can change as new code paths - * are reached. - */ - getCategories(): Promise; - /** - * Get the maximum usage across processes of trace buffer as a percentage of the - * full state. When the TraceBufferUsage value is determined the callback is - * called. Deprecated Soon - */ - getTraceBufferUsage(callback: (value: number) => void): void; - /** - * Get the maximum usage across processes of trace buffer as a percentage of the - * full state. - */ - getTraceBufferUsage(): Promise; - /** - * Start recording on all processes. Recording begins immediately locally and - * asynchronously on child processes as soon as they receive the EnableRecording - * request. The callback will be called once all child processes have acknowledged - * the startRecording request. Deprecated Soon - */ - startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig), callback: Function): void; - /** - * Start recording on all processes. Recording begins immediately locally and - * asynchronously on child processes as soon as they receive the EnableRecording - * request. - */ - startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig)): Promise; - /** - * Stop recording on all processes. Child processes typically cache trace data and - * only rarely flush and send trace data back to the main process. This helps to - * minimize the runtime overhead of tracing since sending trace data over IPC can - * be an expensive operation. So, to end tracing, we must asynchronously ask all - * child processes to flush any pending trace data. Once all child processes have - * acknowledged the stopRecording request, callback will be called with a file that - * contains the traced data. Trace data will be written into resultFilePath if it - * is not empty or into a temporary file. The actual file path will be passed to - * callback if it's not null. Deprecated Soon - */ - stopRecording(resultFilePath: string, callback: (resultFilePath: string) => void): void; - /** - * Stop recording on all processes. Child processes typically cache trace data and - * only rarely flush and send trace data back to the main process. This helps to - * minimize the runtime overhead of tracing since sending trace data over IPC can - * be an expensive operation. So, to end tracing, we must asynchronously ask all - * child processes to flush any pending trace data. Trace data will be written into - * resultFilePath if it is not empty or into a temporary file. - */ - stopRecording(resultFilePath: string): Promise; - } - - interface ContextBridge extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/context-bridge - - exposeInMainWorld(apiKey: string, api: Record): void; - } - - interface Cookie { - - // Docs: http://electronjs.org/docs/api/structures/cookie - - /** - * The domain of the cookie; this will be normalized with a preceding dot so that - * it's also valid for subdomains. - */ - domain?: string; - /** - * The expiration date of the cookie as the number of seconds since the UNIX epoch. - * Not provided for session cookies. - */ - expirationDate?: number; - /** - * Whether the cookie is a host-only cookie; this will only be true if no domain - * was passed. - */ - hostOnly?: boolean; - /** - * Whether the cookie is marked as HTTP only. - */ - httpOnly?: boolean; - /** - * The name of the cookie. - */ - name: string; - /** - * The path of the cookie. - */ - path?: string; - /** - * Whether the cookie is marked as secure. - */ - secure?: boolean; - /** - * Whether the cookie is a session cookie or a persistent cookie with an expiration - * date. - */ - session?: boolean; - /** - * The value of the cookie. - */ - value: string; - } - - class Cookies extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/cookies - - /** - * Emitted when a cookie is changed because it was added, edited, removed, or - * expired. - */ - on(event: 'changed', listener: (event: Event, - /** - * The cookie that was changed. - */ - cookie: Cookie, - /** - * The cause of the change with one of the following values: - */ - cause: ('explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'), - /** - * `true` if the cookie was removed, `false` otherwise. - */ - removed: boolean) => void): this; - once(event: 'changed', listener: (event: Event, - /** - * The cookie that was changed. - */ - cookie: Cookie, - /** - * The cause of the change with one of the following values: - */ - cause: ('explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'), - /** - * `true` if the cookie was removed, `false` otherwise. - */ - removed: boolean) => void): this; - addListener(event: 'changed', listener: (event: Event, - /** - * The cookie that was changed. - */ - cookie: Cookie, - /** - * The cause of the change with one of the following values: - */ - cause: ('explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'), - /** - * `true` if the cookie was removed, `false` otherwise. - */ - removed: boolean) => void): this; - removeListener(event: 'changed', listener: (event: Event, - /** - * The cookie that was changed. - */ - cookie: Cookie, - /** - * The cause of the change with one of the following values: - */ - cause: ('explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'), - /** - * `true` if the cookie was removed, `false` otherwise. - */ - removed: boolean) => void): this; - /** - * Writes any unwritten cookies data to disk. - */ - flushStore(): Promise; - /** - * Writes any unwritten cookies data to disk. Deprecated Soon - */ - flushStore(callback: Function): void; - /** - * Sends a request to get all cookies matching filter, and resolves a promise with - * the response. - */ - get(filter: Filter): Promise; - /** - * Sends a request to get all cookies matching filter, callback will be called with - * callback(error, cookies) on complete. Deprecated Soon - */ - get(filter: Filter, callback: (error: Error, cookies: Cookie[]) => void): void; - /** - * Removes the cookies matching url and name - */ - remove(url: string, name: string): Promise; - /** - * Removes the cookies matching url and name, callback will called with callback() - * on complete. Deprecated Soon - */ - remove(url: string, name: string, callback: Function): void; - /** - * Sets a cookie with details. - */ - set(details: Details): Promise; - /** - * Sets a cookie with details, callback will be called with callback(error) on - * complete. Deprecated Soon - */ - set(details: Details, callback: (error: Error) => void): void; - } - - interface CPUUsage { - - // Docs: http://electronjs.org/docs/api/structures/cpu-usage - - /** - * The number of average idle cpu wakeups per second since the last call to - * getCPUUsage. First call returns 0. Will always return 0 on Windows. - */ - idleWakeupsPerSecond: number; - /** - * Percentage of CPU used since the last call to getCPUUsage. First call returns 0. - */ - percentCPUUsage: number; - } - - interface CrashReport { - - // Docs: http://electronjs.org/docs/api/structures/crash-report - - date: Date; - id: string; - } - - interface CrashReporter { - - // Docs: http://electronjs.org/docs/api/crash-reporter - - /** - * 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. This API is only available on macOS and windows, if you need to - * add/update extra parameters on Linux after your first call to start you can call - * start again with the updated extra options. - */ - addExtraParameter(key: string, value: string): void; - /** - * Returns the date and ID of the last crash report. Only crash reports that have - * been uploaded will be returned; even if a crash report is present on disk it - * will not be returned until it is uploaded. In the case that there are no - * uploaded reports, null is returned. - */ - getLastCrashReport(): CrashReport; - /** - * See all of the current parameters being passed to the crash reporter. - */ - getParameters(): void; - /** - * Returns all uploaded crash reports. Each report contains the date and uploaded - * ID. - */ - getUploadedReports(): CrashReport[]; - /** - * Note: This API can only be called from the main process. - */ - getUploadToServer(): boolean; - /** - * Remove a extra parameter from the current set of parameters so that it will not - * be sent with the crash report. - */ - removeExtraParameter(key: string): void; - /** - * This would normally be controlled by user preferences. This has no effect if - * called before start is called. Note: This API can only be called from the main - * process. - */ - setUploadToServer(uploadToServer: boolean): void; - /** - * You are required to call this method before using any other crashReporter APIs - * and in each process (main/renderer) from which you want to collect crash - * reports. You can pass different options to crashReporter.start when calling from - * different processes. Note Child processes created via the child_process module - * will not have access to the Electron modules. Therefore, to collect crash - * reports from them, use process.crashReporter.start instead. Pass the same - * options as above along with an additional one called crashesDirectory that - * should point to a directory to store the crash reports temporarily. You can test - * this out by calling process.crash() to crash the child process. Note: If you - * need send additional/updated extra parameters after your first call start you - * can call addExtraParameter on macOS or call start again with the new/updated - * extra parameters on Linux and Windows. Note: On macOS and windows, Electron uses - * a new crashpad client for crash collection and reporting. If you want to enable - * crash reporting, initializing crashpad from the main process using - * crashReporter.start is required regardless of which process you want to collect - * crashes from. Once initialized this way, the crashpad handler collects crashes - * from all processes. You still have to call crashReporter.start from the renderer - * or child process, otherwise crashes from them will get reported without - * companyName, productName or any of the extra information. - */ - start(options: CrashReporterStartOptions): void; - } - - interface CustomScheme { - - // Docs: http://electronjs.org/docs/api/structures/custom-scheme - - privileges?: Privileges; - /** - * Custom schemes to be registered with options. - */ - scheme: string; - } - - class Debugger extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/debugger - - /** - * Emitted when debugging session is terminated. This happens either when - * webContents is closed or devtools is invoked for the attached webContents. - */ - on(event: 'detach', listener: (event: Event, - /** - * Reason for detaching debugger. - */ - reason: string) => void): this; - once(event: 'detach', listener: (event: Event, - /** - * Reason for detaching debugger. - */ - reason: string) => void): this; - addListener(event: 'detach', listener: (event: Event, - /** - * Reason for detaching debugger. - */ - reason: string) => void): this; - removeListener(event: 'detach', listener: (event: Event, - /** - * Reason for detaching debugger. - */ - reason: string) => void): this; - /** - * Emitted whenever debugging target issues instrumentation event. - */ - on(event: 'message', listener: (event: Event, - /** - * Method name. - */ - method: string, - /** - * Event parameters defined by the 'parameters' attribute in the remote debugging - * protocol. - */ - params: any) => void): this; - once(event: 'message', listener: (event: Event, - /** - * Method name. - */ - method: string, - /** - * Event parameters defined by the 'parameters' attribute in the remote debugging - * protocol. - */ - params: any) => void): this; - addListener(event: 'message', listener: (event: Event, - /** - * Method name. - */ - method: string, - /** - * Event parameters defined by the 'parameters' attribute in the remote debugging - * protocol. - */ - params: any) => void): this; - removeListener(event: 'message', listener: (event: Event, - /** - * Method name. - */ - method: string, - /** - * Event parameters defined by the 'parameters' attribute in the remote debugging - * protocol. - */ - params: any) => void): this; - /** - * Attaches the debugger to the webContents. - */ - attach(protocolVersion?: string): void; - /** - * Detaches the debugger from the webContents. - */ - detach(): void; - isAttached(): boolean; - /** - * Send given command to the debugging target. Deprecated Soon - */ - sendCommand(method: string, commandParams?: any, callback?: (error: any, result: any) => void): void; - /** - * Send given command to the debugging target. - */ - sendCommand(method: string, commandParams?: any): Promise; - } - - interface DesktopCapturer extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/desktop-capturer - - /** - * Starts gathering information about all available desktop media sources, and - * calls callback(error, sources) when finished. sources is an array of - * DesktopCapturerSource objects, each DesktopCapturerSource represents a screen or - * an individual window that can be captured. Deprecated Soon - */ - getSources(options: SourcesOptions, callback: (error: Error, sources: DesktopCapturerSource[]) => void): void; - getSources(options: SourcesOptions): Promise; - } - - interface DesktopCapturerSource { - - // Docs: http://electronjs.org/docs/api/structures/desktop-capturer-source - - /** - * An icon image of the application that owns the window or null if the source has - * a type screen. The size of the icon is not known in advance and depends on what - * the the application provides. - */ - appIcon: NativeImage; - /** - * A unique identifier that will correspond to the id of the matching returned by - * the . On some platforms, this is equivalent to the XX portion of the id field - * above and on others it will differ. It will be an empty string if not available. - */ - display_id: string; - /** - * The identifier of a window or screen that can be used as a chromeMediaSourceId - * constraint when calling [navigator.webkitGetUserMedia]. The format of the - * identifier will be window:XX or screen:XX, where XX is a random generated - * number. - */ - id: string; - /** - * A screen source will be named either Entire Screen or Screen , while the name of - * a window source will match the window title. - */ - name: string; - /** - * A thumbnail image. There is no guarantee that the size of the thumbnail is the - * same as the thumbnailSize specified in the options passed to - * desktopCapturer.getSources. The actual size depends on the scale of the screen - * or window. - */ - thumbnail: NativeImage; - } - - interface Dialog extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/dialog - - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: Deprecated Soon - */ - showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions, callback: Function): void; - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: - */ - showCertificateTrustDialog(options: CertificateTrustDialogOptions): Promise; - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: Deprecated Soon - */ - showCertificateTrustDialog(options: CertificateTrustDialogOptions, callback: Function): void; - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: - */ - showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions): Promise; - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: Deprecated Soon - */ - showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions, callback: Function): void; - /** - * Displays a modal dialog that shows an error message. This API can be called - * safely before the ready event the app module emits, it is usually used to report - * errors in early stage of startup. If called before the app readyevent on Linux, - * the message will be emitted to stderr, and no GUI dialog will appear. - */ - showErrorBox(title: string, content: string): void; - /** - * Shows a message box, it will block the process until the message box is closed. - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. - */ - showMessageBox(browserWindow: BrowserWindow, options: MessageBoxOptions): Promise; - /** - * Shows a message box, it will block the process until the message box is closed. - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. - */ - showMessageBox(options: MessageBoxOptions): Promise; - /** - * Shows a message box, it will block the process until the message box is closed. - * It returns the index of the clicked button. The browserWindow argument allows - * the dialog to attach itself to a parent window, making it modal. - */ - showMessageBoxSync(browserWindow: BrowserWindow, options: MessageBoxSyncOptions): number; - /** - * Shows a message box, it will block the process until the message box is closed. - * It returns the index of the clicked button. The browserWindow argument allows - * the dialog to attach itself to a parent window, making it modal. - */ - showMessageBoxSync(options: MessageBoxSyncOptions): number; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed or selected when you want to limit the user to a specific type. For - * example: The extensions array should contain extensions without wildcards or - * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an - * open dialog can not be both a file selector and a directory selector, so if you - * set properties to ['openFile', 'openDirectory'] on these platforms, a directory - * selector will be shown. - */ - showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: Function): Promise; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed or selected when you want to limit the user to a specific type. For - * example: The extensions array should contain extensions without wildcards or - * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an - * open dialog can not be both a file selector and a directory selector, so if you - * set properties to ['openFile', 'openDirectory'] on these platforms, a directory - * selector will be shown. - */ - showOpenDialog(options: OpenDialogOptions, callback?: Function): Promise; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed or selected when you want to limit the user to a specific type. For - * example: The extensions array should contain extensions without wildcards or - * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an - * open dialog can not be both a file selector and a directory selector, so if you - * set properties to ['openFile', 'openDirectory'] on these platforms, a directory - * selector will be shown. - */ - showOpenDialogSync(browserWindow: BrowserWindow, options: OpenDialogSyncOptions): (string[]) | (undefined); - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed or selected when you want to limit the user to a specific type. For - * example: The extensions array should contain extensions without wildcards or - * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an - * open dialog can not be both a file selector and a directory selector, so if you - * set properties to ['openFile', 'openDirectory'] on these platforms, a directory - * selector will be shown. - */ - showOpenDialogSync(options: OpenDialogSyncOptions): (string[]) | (undefined); - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. Note: On macOS, using - * the asynchronous version is recommended to avoid issues when expanding and - * collapsing the dialog. - */ - showSaveDialog(options: SaveDialogOptions): Promise; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. Note: On macOS, using - * the asynchronous version is recommended to avoid issues when expanding and - * collapsing the dialog. - */ - showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions): Promise; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. - */ - showSaveDialogSync(options: SaveDialogSyncOptions): (string) | (undefined); - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. - */ - showSaveDialogSync(browserWindow: BrowserWindow, options: SaveDialogSyncOptions): (string) | (undefined); - } - - interface Display { - - // Docs: http://electronjs.org/docs/api/structures/display - - /** - * Can be available, unavailable, unknown. - */ - accelerometerSupport: ('available' | 'unavailable' | 'unknown'); - bounds: Rectangle; - /** - * The number of bits per pixel. - */ - colorDepth: number; - /** - * represent a color space (three-dimensional object which contains all realizable - * color combinations) for the purpose of color conversions - */ - colorSpace: string; - /** - * The number of bits per color component. - */ - depthPerComponent: number; - /** - * Unique identifier associated with the display. - */ - id: number; - /** - * true for an internal display and false for an external display - */ - internal: boolean; - /** - * Whether or not the display is a monochrome display. - */ - monochrome: boolean; - /** - * Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees. - */ - rotation: number; - /** - * Output device's pixel scale factor. - */ - scaleFactor: number; - size: Size; - /** - * Can be available, unavailable, unknown. - */ - touchSupport: ('available' | 'unavailable' | 'unknown'); - workArea: Rectangle; - workAreaSize: Size; - } - - class DownloadItem extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/download-item - - /** - * Emitted when the download is in a terminal state. This includes a completed - * download, a cancelled download (via downloadItem.cancel()), and interrupted - * download that can't be resumed. The state can be one of following: - */ - on(event: 'done', listener: (event: Event, - /** - * Can be `completed`, `cancelled` or `interrupted`. - */ - state: ('completed' | 'cancelled' | 'interrupted')) => void): this; - once(event: 'done', listener: (event: Event, - /** - * Can be `completed`, `cancelled` or `interrupted`. - */ - state: ('completed' | 'cancelled' | 'interrupted')) => void): this; - addListener(event: 'done', listener: (event: Event, - /** - * Can be `completed`, `cancelled` or `interrupted`. - */ - state: ('completed' | 'cancelled' | 'interrupted')) => void): this; - removeListener(event: 'done', listener: (event: Event, - /** - * Can be `completed`, `cancelled` or `interrupted`. - */ - state: ('completed' | 'cancelled' | 'interrupted')) => void): this; - /** - * Emitted when the download has been updated and is not done. The state can be one - * of following: - */ - on(event: 'updated', listener: (event: Event, - /** - * Can be `progressing` or `interrupted`. - */ - state: ('progressing' | 'interrupted')) => void): this; - once(event: 'updated', listener: (event: Event, - /** - * Can be `progressing` or `interrupted`. - */ - state: ('progressing' | 'interrupted')) => void): this; - addListener(event: 'updated', listener: (event: Event, - /** - * Can be `progressing` or `interrupted`. - */ - state: ('progressing' | 'interrupted')) => void): this; - removeListener(event: 'updated', listener: (event: Event, - /** - * Can be `progressing` or `interrupted`. - */ - state: ('progressing' | 'interrupted')) => void): this; - /** - * Cancels the download operation. - */ - cancel(): void; - canResume(): boolean; - getContentDisposition(): string; - getETag(): string; - /** - * Note: The file name is not always the same as the actual one saved in local - * disk. If user changes the file name in a prompted download saving dialog, the - * actual name of saved file will be different. - */ - getFilename(): string; - getLastModifiedTime(): string; - getMimeType(): string; - getReceivedBytes(): number; - getSaveDialogOptions(): SaveDialogOptions; - getSavePath(): string; - getStartTime(): number; - /** - * Note: The following methods are useful specifically to resume a cancelled item - * when session is restarted. - */ - getState(): ('progressing' | 'completed' | 'cancelled' | 'interrupted'); - /** - * If the size is unknown, it returns 0. - */ - getTotalBytes(): number; - getURL(): string; - getURLChain(): string[]; - hasUserGesture(): boolean; - isPaused(): boolean; - /** - * Pauses the download. - */ - pause(): void; - /** - * Resumes the download that has been paused. Note: To enable resumable downloads - * the server you are downloading from must support range requests and provide both - * Last-Modified and ETag header values. Otherwise resume() will dismiss previously - * received bytes and restart the download from the beginning. - */ - resume(): void; - /** - * This API allows the user to set custom options for the save dialog that opens - * for the download item by default. The API is only available in session's - * will-download callback function. - */ - setSaveDialogOptions(options: SaveDialogOptions): void; - /** - * The API is only available in session's will-download callback function. If user - * doesn't set the save path via the API, Electron will use the original routine to - * determine the save path(Usually prompts a save dialog). - */ - setSavePath(path: string): void; - } - - interface Event extends GlobalEvent { - - // Docs: http://electronjs.org/docs/api/structures/event - - preventDefault: (() => void); - } - - interface FileFilter { - - // Docs: http://electronjs.org/docs/api/structures/file-filter - - extensions: string[]; - name: string; - } - - interface GlobalShortcut extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/global-shortcut - - /** - * When the accelerator is already taken by other applications, this call will - * still return false. This behavior is intended by operating systems, since they - * don't want applications to fight for global shortcuts. - */ - isRegistered(accelerator: Accelerator): boolean; - /** - * Registers a global shortcut of accelerator. The callback is called when the - * registered shortcut is pressed by the user. When the accelerator is already - * taken by other applications, this call will silently fail. This behavior is - * intended by operating systems, since they don't want applications to fight for - * global shortcuts. The following accelerators will not be registered successfully - * on macOS 10.14 Mojave unless the app has been authorized as a trusted - * accessibility client: - */ - register(accelerator: Accelerator, callback: Function): boolean; - /** - * Registers a global shortcut of all accelerator items in accelerators. The - * callback is called when any of the registered shortcuts are pressed by the user. - * When a given accelerator is already taken by other applications, this call will - * silently fail. This behavior is intended by operating systems, since they don't - * want applications to fight for global shortcuts. The following accelerators will - * not be registered successfully on macOS 10.14 Mojave unless the app has been - * authorized as a trusted accessibility client: - */ - registerAll(accelerators: string[], callback: Function): void; - /** - * Unregisters the global shortcut of accelerator. - */ - unregister(accelerator: Accelerator): void; - /** - * Unregisters all of the global shortcuts. - */ - unregisterAll(): void; - } - - interface GPUFeatureStatus { - - // Docs: http://electronjs.org/docs/api/structures/gpu-feature-status - - /** - * Canvas. - */ - '2d_canvas': string; - /** - * Flash. - */ - flash_3d: string; - /** - * Flash Stage3D. - */ - flash_stage3d: string; - /** - * Flash Stage3D Baseline profile. - */ - flash_stage3d_baseline: string; - /** - * Compositing. - */ - gpu_compositing: string; - /** - * Multiple Raster Threads. - */ - multiple_raster_threads: string; - /** - * Native GpuMemoryBuffers. - */ - native_gpu_memory_buffers: string; - /** - * Rasterization. - */ - rasterization: string; - /** - * Video Decode. - */ - video_decode: string; - /** - * Video Encode. - */ - video_encode: string; - /** - * VPx Video Decode. - */ - vpx_decode: string; - /** - * WebGL. - */ - webgl: string; - /** - * WebGL2. - */ - webgl2: string; - } - - interface InAppPurchase extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/in-app-purchase - - /** - * Emitted when one or more transactions have been updated. - */ - on(event: 'transactions-updated', listener: (event: Event, - /** - * Array of objects. - */ - transactions: Transaction[]) => void): this; - once(event: 'transactions-updated', listener: (event: Event, - /** - * Array of objects. - */ - transactions: Transaction[]) => void): this; - addListener(event: 'transactions-updated', listener: (event: Event, - /** - * Array of objects. - */ - transactions: Transaction[]) => void): this; - removeListener(event: 'transactions-updated', listener: (event: Event, - /** - * Array of objects. - */ - transactions: Transaction[]) => void): this; - canMakePayments(): boolean; - /** - * Completes all pending transactions. - */ - finishAllTransactions(): void; - /** - * Completes the pending transactions corresponding to the date. - */ - finishTransactionByDate(date: string): void; - /** - * Retrieves the product descriptions. Deprecated Soon - */ - getProducts(productIDs: string[], callback: (products: Product[]) => void): void; - /** - * Retrieves the product descriptions. - */ - getProducts(productIDs: string[]): Promise; - getReceiptURL(): string; - /** - * You should listen for the transactions-updated event as soon as possible and - * certainly before you call purchaseProduct. Deprecated Soon - */ - purchaseProduct(productID: string, quantity?: number, callback?: (isProductValid: boolean) => void): void; - /** - * You should listen for the transactions-updated event as soon as possible and - * certainly before you call purchaseProduct. - */ - purchaseProduct(productID: string, quantity?: number): Promise; - } - - class IncomingMessage extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/incoming-message - - /** - * Emitted when a request has been canceled during an ongoing HTTP transaction. - */ - on(event: 'aborted', listener: Function): this; - once(event: 'aborted', listener: Function): this; - addListener(event: 'aborted', listener: Function): this; - removeListener(event: 'aborted', listener: Function): this; - /** - * The data event is the usual method of transferring response data into - * applicative code. - */ - on(event: 'data', listener: ( - /** - * A chunk of response body's data. - */ - chunk: Buffer) => void): this; - once(event: 'data', listener: ( - /** - * A chunk of response body's data. - */ - chunk: Buffer) => void): this; - addListener(event: 'data', listener: ( - /** - * A chunk of response body's data. - */ - chunk: Buffer) => void): this; - removeListener(event: 'data', listener: ( - /** - * A chunk of response body's data. - */ - chunk: Buffer) => void): this; - /** - * Indicates that response body has ended. - */ - on(event: 'end', listener: Function): this; - once(event: 'end', listener: Function): this; - addListener(event: 'end', listener: Function): this; - removeListener(event: 'end', listener: Function): this; - /** - * error Error - Typically holds an error string identifying failure root cause. - * Emitted when an error was encountered while streaming response data events. For - * instance, if the server closes the underlying while the response is still - * streaming, an error event will be emitted on the response object and a close - * event will subsequently follow on the request object. - */ - on(event: 'error', listener: Function): this; - once(event: 'error', listener: Function): this; - addListener(event: 'error', listener: Function): this; - removeListener(event: 'error', listener: Function): this; - headers: any; - httpVersion: string; - httpVersionMajor: number; - httpVersionMinor: number; - statusCode: number; - statusMessage: string; - } - - interface IOCounters { - - // Docs: http://electronjs.org/docs/api/structures/io-counters - - /** - * Then number of I/O other operations. - */ - otherOperationCount: number; - /** - * Then number of I/O other transfers. - */ - otherTransferCount: number; - /** - * The number of I/O read operations. - */ - readOperationCount: number; - /** - * The number of I/O read transfers. - */ - readTransferCount: number; - /** - * The number of I/O write operations. - */ - writeOperationCount: number; - /** - * The number of I/O write transfers. - */ - writeTransferCount: number; - } - - interface IpcMain extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/ipc-main - - /** - * Listens to channel, when a new message arrives listener would be called with - * listener(event, args...). - */ - on(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this; - /** - * 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: IpcMainEvent, ...args: any[]) => void): this; - /** - * Removes listeners of the specified channel. - */ - removeAllListeners(channel: string): this; - /** - * Removes the specified listener from the listener array for the specified - * channel. - */ - removeListener(channel: string, listener: Function): this; - } - - interface IpcMainEvent extends Event { - - // Docs: http://electronjs.org/docs/api/structures/ipc-main-event - - /** - * The ID of the renderer frame that sent this message - */ - frameId: number; - /** - * A function that will send an IPC message to the renderer frame that sent the - * original message that you are currently handling. You should use this method to - * "reply" to the sent message in order to guaruntee the reply will go to the - * correct process and frame. - */ - reply: Function; - /** - * Set this to the value to be returned in a syncronous message - */ - returnValue: any; - /** - * Returns the webContents that sent the message - */ - sender: WebContents; - } - - interface IpcRenderer extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/ipc-renderer - - /** - * Listens to channel, when a new message arrives listener would be called with - * listener(event, args...). - */ - on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): this; - /** - * 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: IpcRendererEvent, ...args: any[]) => void): this; - /** - * Removes all listeners, or those of the specified channel. - */ - removeAllListeners(channel: string): this; - /** - * Removes the specified listener from the listener array for the specified - * channel. - */ - removeListener(channel: string, listener: Function): this; - /** - * Send a message to the main process asynchronously via channel, you can also send - * arbitrary arguments. Arguments will be serialized in JSON internally and hence - * no functions or prototype chain will be included. The main process handles it by - * listening for channel with ipcMain module. - */ - send(channel: string, ...args: any[]): void; - /** - * Send a message to the main process synchronously via channel, you can also send - * arbitrary arguments. Arguments will be serialized in JSON internally and hence - * no functions or prototype chain will be included. The main process handles it by - * listening for channel with ipcMain module, and replies by setting - * event.returnValue. Note: Sending a synchronous message will block the whole - * renderer process, unless you know what you are doing you should never use it. - */ - // sendSync(channel: string, ...args: any[]): any; ### VSCODE CHANGE (we do not want to use sendSync) - /** - * Sends a message to a window with webContentsId via channel. - */ - sendTo(webContentsId: number, channel: string, ...args: any[]): void; - /** - * Like ipcRenderer.send but the event will be sent to the element in the - * host page instead of the main process. - */ - sendToHost(channel: string, ...args: any[]): void; - } - - interface IpcRendererEvent extends Event { - - // Docs: http://electronjs.org/docs/api/structures/ipc-renderer-event - - /** - * The IpcRenderer instance that emitted the event originally - */ - sender: IpcRenderer; - /** - * The webContents.id that sent the message, you can call - * event.sender.sendTo(event.senderId, ...) to reply to the message, see for more - * information. This only applies to messages sent from a different renderer. - * Messages sent directly from the main process set event.senderId to 0. - */ - senderId: number; - } - - interface JumpListCategory { - - // Docs: http://electronjs.org/docs/api/structures/jump-list-category - - /** - * Array of objects if type is tasks or custom, otherwise it should be omitted. - */ - items?: JumpListItem[]; - /** - * Must be set if type is custom, otherwise it should be omitted. - */ - name?: string; - /** - * One of the following: - */ - type?: ('tasks' | 'frequent' | 'recent' | 'custom'); - } - - interface JumpListItem { - - // Docs: http://electronjs.org/docs/api/structures/jump-list-item - - /** - * The command line arguments when program is executed. Should only be set if type - * is task. - */ - args?: string; - /** - * Description of the task (displayed in a tooltip). Should only be set if type is - * task. - */ - description?: string; - /** - * The index of the icon in the resource file. If a resource file contains multiple - * icons this value can be used to specify the zero-based index of the icon that - * should be displayed for this task. If a resource file contains only one icon, - * this property should be set to zero. - */ - iconIndex?: number; - /** - * The absolute path to an icon to be displayed in a Jump List, which can be an - * arbitrary resource file that contains an icon (e.g. .ico, .exe, .dll). You can - * usually specify process.execPath to show the program icon. - */ - iconPath?: string; - /** - * Path of the file to open, should only be set if type is file. - */ - path?: string; - /** - * Path of the program to execute, usually you should specify process.execPath - * which opens the current program. Should only be set if type is task. - */ - program?: string; - /** - * The text to be displayed for the item in the Jump List. Should only be set if - * type is task. - */ - title?: string; - /** - * One of the following: - */ - type?: ('task' | 'separator' | 'file'); - /** - * The working directory. Default is empty. - */ - workingDirectory?: string; - } - - interface KeyboardEvent extends Event { - - // Docs: http://electronjs.org/docs/api/structures/keyboard-event - - /** - * whether an Alt key was used in an accelerator to trigger the Event - */ - altKey?: boolean; - /** - * whether the Control key was used in an accelerator to trigger the Event - */ - ctrlKey?: boolean; - /** - * whether a meta key was used in an accelerator to trigger the Event - */ - metaKey?: boolean; - /** - * whether a Shift key was used in an accelerator to trigger the Event - */ - shiftKey?: boolean; - /** - * whether an accelerator was used to trigger the event as opposed to another user - * gesture like mouse click - */ - triggeredByAccelerator?: boolean; - } - - interface MemoryUsageDetails { - - // Docs: http://electronjs.org/docs/api/structures/memory-usage-details - - count: number; - liveSize: number; - size: number; - } - - class Menu { - - // Docs: http://electronjs.org/docs/api/menu - - /** - * Emitted when a popup is closed either manually or with menu.closePopup(). - */ - on(event: 'menu-will-close', listener: (event: Event) => void): this; - once(event: 'menu-will-close', listener: (event: Event) => void): this; - addListener(event: 'menu-will-close', listener: (event: Event) => void): this; - removeListener(event: 'menu-will-close', listener: (event: Event) => void): this; - /** - * Emitted when menu.popup() is called. - */ - on(event: 'menu-will-show', listener: (event: Event) => void): this; - once(event: 'menu-will-show', listener: (event: Event) => void): this; - addListener(event: 'menu-will-show', listener: (event: Event) => void): this; - removeListener(event: 'menu-will-show', listener: (event: Event) => void): this; - constructor(); - /** - * Generally, the template is an array of options for constructing a MenuItem. The - * usage can be referenced above. You can also attach other fields to the element - * of the template and they will become properties of the constructed menu items. - */ - static buildFromTemplate(template: Array<(MenuItemConstructorOptions) | (MenuItem)>): Menu; - /** - * Note: The returned Menu instance doesn't support dynamic addition or removal of - * menu items. Instance properties can still be dynamically modified. - */ - static getApplicationMenu(): (Menu) | (null); - /** - * Sends the action to the first responder of application. This is used for - * emulating default macOS menu behaviors. Usually you would use the role property - * of a MenuItem. See the macOS Cocoa Event Handling Guide for more information on - * macOS' native actions. - */ - static sendActionToFirstResponder(action: string): void; - /** - * Sets menu as the application menu on macOS. On Windows and Linux, the menu will - * be set as each window's top menu. Also on Windows and Linux, you can use a & in - * the top-level item name to indicate which letter should get a generated - * accelerator. For example, using &File for the file menu would result in a - * generated Alt-F accelerator that opens the associated menu. The indicated - * character in the button label gets an underline. The & character is not - * displayed on the button label. Passing null will suppress the default menu. On - * Windows and Linux, this has the additional effect of removing the menu bar from - * the window. Note: The default menu will be created automatically if the app does - * not set one. It contains standard items such as File, Edit, View, Window and - * Help. - */ - static setApplicationMenu(menu: (Menu) | (null)): void; - /** - * Appends the menuItem to the menu. - */ - append(menuItem: MenuItem): void; - /** - * Closes the context menu in the browserWindow. - */ - closePopup(browserWindow?: BrowserWindow): void; - getMenuItemById(id: string): MenuItem; - /** - * Inserts the menuItem to the pos position of the menu. - */ - insert(pos: number, menuItem: MenuItem): void; - /** - * Pops up this menu as a context menu in the BrowserWindow. - */ - popup(options?: PopupOptions): void; - items: MenuItem[]; - } - - class MenuItem { - - // Docs: http://electronjs.org/docs/api/menu-item - - constructor(options: MenuItemConstructorOptions); - accelerator: string; - checked: boolean; - click: Function; - commandId: number; - enabled: boolean; - icon: NativeImage; - id: string; - label: string; - menu: Menu; - registerAccelerator: boolean; - role: string; - sublabel: string; - submenu: Menu; - type: string; - visible: boolean; - } - - interface MimeTypedBuffer { - - // Docs: http://electronjs.org/docs/api/structures/mime-typed-buffer - - /** - * The actual Buffer content. - */ - data: Buffer; - /** - * The mimeType of the Buffer that you are sending. - */ - mimeType: string; - } - - class NativeImage { - - // Docs: http://electronjs.org/docs/api/native-image - - /** - * Creates an empty NativeImage instance. - */ - static createEmpty(): NativeImage; - /** - * Creates a new NativeImage instance from buffer that contains the raw bitmap - * pixel data returned by toBitmap(). The specific format is platform-dependent. - */ - static createFromBitmap(buffer: Buffer, options: CreateFromBitmapOptions): NativeImage; - /** - * Creates a new NativeImage instance from buffer. Tries to decode as PNG or JPEG - * first. - */ - static createFromBuffer(buffer: Buffer, options?: CreateFromBufferOptions): NativeImage; - /** - * Creates a new NativeImage instance from dataURL. - */ - static createFromDataURL(dataURL: string): NativeImage; - /** - * Creates a new NativeImage instance from the NSImage that maps to the given image - * name. See NSImageName for a list of possible values. The hslShift is applied to - * the image with the following rules This means that [-1, 0, 1] will make the - * image completely white and [-1, 1, 0] will make the image completely black. In - * some cases, the NSImageName doesn't match its string representation; one example - * of this is NSFolderImageName, whose string representation would actually be - * NSFolder. Therefore, you'll need to determine the correct string representation - * for your image before passing it in. This can be done with the following: echo - * -e '#import \nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' | - * clang -otest -x objective-c -framework Cocoa - && ./test where SYSTEM_IMAGE_NAME - * should be replaced with any value from this list. - */ - static createFromNamedImage(imageName: string, hslShift: number[]): NativeImage; - /** - * Creates a new NativeImage instance from a file located at path. This method - * returns an empty image if the path does not exist, cannot be read, or is not a - * valid image. - */ - static createFromPath(path: string): NativeImage; - /** - * Add an image representation for a specific scale factor. This can be used to - * explicitly add different scale factor representations to an image. This can be - * called on empty images. - */ - addRepresentation(options: AddRepresentationOptions): void; - crop(rect: Rectangle): NativeImage; - getAspectRatio(): number; - /** - * The difference between getBitmap() and toBitmap() is, getBitmap() does not copy - * the bitmap data, so you have to use the returned Buffer immediately in current - * event loop tick, otherwise the data might be changed or destroyed. - */ - getBitmap(options?: BitmapOptions): Buffer; - /** - * Notice that the returned pointer is a weak pointer to the underlying native - * image instead of a copy, so you must ensure that the associated nativeImage - * instance is kept around. - */ - getNativeHandle(): Buffer; - getSize(): Size; - isEmpty(): boolean; - isTemplateImage(): boolean; - /** - * If only the height or the width are specified then the current aspect ratio will - * be preserved in the resized image. - */ - resize(options: ResizeOptions): NativeImage; - /** - * Marks the image as a template image. - */ - setTemplateImage(option: boolean): void; - toBitmap(options?: ToBitmapOptions): Buffer; - toDataURL(options?: ToDataURLOptions): string; - toJPEG(quality: number): Buffer; - toPNG(options?: ToPNGOptions): Buffer; - } - - interface Net extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/net - - /** - * Creates a ClientRequest instance using the provided options which are directly - * forwarded to the ClientRequest constructor. The net.request method would be used - * to issue both secure and insecure HTTP requests according to the specified - * protocol scheme in the options object. - */ - request(options: (any) | (string)): ClientRequest; - } - - interface NetLog extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/net-log - - /** - * Starts recording network events to path. - */ - startLogging(path: string): void; - /** - * Stops recording network events. If not called, net logging will automatically - * end when app quits. Deprecated Soon - */ - stopLogging(callback?: (path: string) => void): void; - /** - * Stops recording network events. If not called, net logging will automatically - * end when app quits. - */ - stopLogging(): Promise; - /** - * A Boolean property that indicates whether network logs are recorded. - */ - currentlyLogging?: boolean; - /** - * A String property that returns the path to the current log file. - */ - currentlyLoggingPath?: string; - } - - class Notification extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/notification - - on(event: 'action', listener: (event: Event, - /** - * The index of the action that was activated. - */ - index: number) => void): this; - once(event: 'action', listener: (event: Event, - /** - * The index of the action that was activated. - */ - index: number) => void): this; - addListener(event: 'action', listener: (event: Event, - /** - * The index of the action that was activated. - */ - index: number) => void): this; - removeListener(event: 'action', listener: (event: Event, - /** - * The index of the action that was activated. - */ - index: number) => void): this; - /** - * Emitted when the notification is clicked by the user. - */ - on(event: 'click', listener: (event: Event) => void): this; - once(event: 'click', listener: (event: Event) => void): this; - addListener(event: 'click', listener: (event: Event) => void): this; - removeListener(event: 'click', listener: (event: Event) => void): this; - /** - * Emitted when the notification is closed by manual intervention from the user. - * This event is not guaranteed to be emitted in all cases where the notification - * is closed. - */ - on(event: 'close', listener: (event: Event) => void): this; - once(event: 'close', listener: (event: Event) => void): this; - addListener(event: 'close', listener: (event: Event) => void): this; - removeListener(event: 'close', listener: (event: Event) => void): this; - /** - * Emitted when the user clicks the "Reply" button on a notification with hasReply: - * true. - */ - on(event: 'reply', listener: (event: Event, - /** - * The string the user entered into the inline reply field. - */ - reply: string) => void): this; - once(event: 'reply', listener: (event: Event, - /** - * The string the user entered into the inline reply field. - */ - reply: string) => void): this; - addListener(event: 'reply', listener: (event: Event, - /** - * The string the user entered into the inline reply field. - */ - reply: string) => void): this; - removeListener(event: 'reply', listener: (event: Event, - /** - * The string the user entered into the inline reply field. - */ - reply: string) => void): this; - /** - * Emitted when the notification is shown to the user, note this could be fired - * multiple times as a notification can be shown multiple times through the show() - * method. - */ - on(event: 'show', listener: (event: Event) => void): this; - once(event: 'show', listener: (event: Event) => void): this; - addListener(event: 'show', listener: (event: Event) => void): this; - removeListener(event: 'show', listener: (event: Event) => void): this; - constructor(options: NotificationConstructorOptions); - static isSupported(): boolean; - /** - * Dismisses the notification. - */ - close(): void; - /** - * Immediately shows the notification to the user, please note this means unlike - * the HTML5 Notification implementation, instantiating a new Notification does not - * immediately show it to the user, you need to call this method before the OS will - * display it. If the notification has been shown before, this method will dismiss - * the previously shown notification and create a new one with identical - * properties. - */ - show(): void; - } - - interface NotificationAction { - - // Docs: http://electronjs.org/docs/api/structures/notification-action - - /** - * The label for the given action. - */ - text?: string; - /** - * The type of action, can be button. - */ - type: ('button'); - } - - interface Point { - - // Docs: http://electronjs.org/docs/api/structures/point - - x: number; - y: number; - } - - interface PowerMonitor extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/power-monitor - - /** - * Emitted when the system is about to lock the screen. - */ - on(event: 'lock-screen', listener: Function): this; - once(event: 'lock-screen', listener: Function): this; - addListener(event: 'lock-screen', listener: Function): this; - removeListener(event: 'lock-screen', listener: Function): this; - /** - * Emitted when the system changes to AC power. - */ - on(event: 'on-ac', listener: Function): this; - once(event: 'on-ac', listener: Function): this; - addListener(event: 'on-ac', listener: Function): this; - removeListener(event: 'on-ac', listener: Function): this; - /** - * Emitted when system changes to battery power. - */ - on(event: 'on-battery', listener: Function): this; - once(event: 'on-battery', listener: Function): this; - addListener(event: 'on-battery', listener: Function): this; - removeListener(event: 'on-battery', listener: Function): this; - /** - * Emitted when system is resuming. - */ - on(event: 'resume', listener: Function): this; - once(event: 'resume', listener: Function): this; - addListener(event: 'resume', listener: Function): this; - removeListener(event: 'resume', listener: Function): this; - /** - * Emitted when the system is about to reboot or shut down. If the event handler - * invokes e.preventDefault(), Electron will attempt to delay system shutdown in - * order for the app to exit cleanly. If e.preventDefault() is called, the app - * should exit as soon as possible by calling something like app.quit(). - */ - on(event: 'shutdown', listener: Function): this; - once(event: 'shutdown', listener: Function): this; - addListener(event: 'shutdown', listener: Function): this; - removeListener(event: 'shutdown', listener: Function): this; - /** - * Emitted when the system is suspending. - */ - on(event: 'suspend', listener: Function): this; - once(event: 'suspend', listener: Function): this; - addListener(event: 'suspend', listener: Function): this; - removeListener(event: 'suspend', listener: Function): this; - /** - * Emitted as soon as the systems screen is unlocked. - */ - on(event: 'unlock-screen', listener: Function): this; - once(event: 'unlock-screen', listener: Function): this; - addListener(event: 'unlock-screen', listener: Function): this; - removeListener(event: 'unlock-screen', listener: Function): this; - /** - * Calculate the system idle state. idleThreshold is the amount of time (in - * seconds) before considered idle. locked is available on supported systems only. - */ - getSystemIdleState(idleThreshold: number): ('active' | 'idle' | 'locked' | 'unknown'); - /** - * Calculate system idle time in seconds. - */ - getSystemIdleTime(): number; - /** - * Calculate the system idle state. idleThreshold is the amount of time (in - * seconds) before considered idle. callback will be called synchronously on some - * systems and with an idleState argument that describes the system's state. locked - * is available on supported systems only. - */ - querySystemIdleState(idleThreshold: number, callback: (idleState: 'active' | 'idle' | 'locked' | 'unknown') => void): void; - /** - * Calculate system idle time in seconds. - */ - querySystemIdleTime(callback: (idleTime: number) => void): void; - } - - interface PowerSaveBlocker extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/power-save-blocker - - isStarted(id: number): boolean; - /** - * Starts preventing the system from entering lower-power mode. Returns an integer - * identifying the power save blocker. Note: prevent-display-sleep has higher - * precedence over prevent-app-suspension. Only the highest precedence type takes - * effect. In other words, prevent-display-sleep always takes precedence over - * prevent-app-suspension. For example, an API calling A requests for - * prevent-app-suspension, and another calling B requests for - * prevent-display-sleep. prevent-display-sleep will be used until B stops its - * request. After that, prevent-app-suspension is used. - */ - start(type: 'prevent-app-suspension' | 'prevent-display-sleep'): number; - /** - * Stops the specified power save blocker. - */ - stop(id: number): void; - } - - interface PrinterInfo { - - // Docs: http://electronjs.org/docs/api/structures/printer-info - - description: string; - isDefault: boolean; - name: string; - status: number; - } - - 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; - /** - * and The amount of memory currently pinned to actual physical RAM in Kilobytes. - */ - residentSet: number; - /** - * The amount of memory shared between processes, typically memory consumed by the - * Electron code itself in Kilobytes. - */ - shared: number; - } - - interface ProcessMetric { - - // Docs: http://electronjs.org/docs/api/structures/process-metric - - /** - * CPU usage of the process. - */ - cpu: CPUUsage; - /** - * Process id of the process. - */ - pid: number; - /** - * Process type. One of the following values: - */ - type: ('Browser' | 'Tab' | 'Utility' | 'Zygote' | 'GPU' | 'Unknown'); - } - - interface Product { - - // Docs: http://electronjs.org/docs/api/structures/product - - /** - * The total size of the content, in bytes. - */ - contentLengths: number[]; - /** - * A string that identifies the version of the content. - */ - contentVersion: string; - /** - * The locale formatted price of the product. - */ - formattedPrice: string; - /** - * A Boolean value that indicates whether the App Store has downloadable content - * for this product. true if at least one file has been associated with the - * product. - */ - isDownloadable: boolean; - /** - * A description of the product. - */ - localizedDescription: string; - /** - * The name of the product. - */ - localizedTitle: string; - /** - * The cost of the product in the local currency. - */ - price: number; - /** - * The string that identifies the product to the Apple App Store. - */ - productIdentifier: string; - } - - interface Protocol extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/protocol - - /** - * Intercepts scheme protocol and uses handler as the protocol's new handler which - * sends a Buffer as a response. - */ - interceptBufferProtocol(scheme: string, handler: (request: InterceptBufferProtocolRequest, callback: (buffer?: Buffer) => void) => void, completion?: (error: Error) => void): void; - /** - * Intercepts scheme protocol and uses handler as the protocol's new handler which - * sends a file as a response. - */ - interceptFileProtocol(scheme: string, handler: (request: InterceptFileProtocolRequest, callback: (filePath: string) => void) => void, completion?: (error: Error) => void): void; - /** - * Intercepts scheme protocol and uses handler as the protocol's new handler which - * sends a new HTTP request as a response. - */ - interceptHttpProtocol(scheme: string, handler: (request: InterceptHttpProtocolRequest, callback: (redirectRequest: RedirectRequest) => void) => void, completion?: (error: Error) => void): void; - /** - * Same as protocol.registerStreamProtocol, except that it replaces an existing - * protocol handler. - */ - interceptStreamProtocol(scheme: string, handler: (request: InterceptStreamProtocolRequest, callback: (stream?: (NodeJS.ReadableStream) | (StreamProtocolResponse)) => void) => void, completion?: (error: Error) => void): void; - /** - * Intercepts scheme protocol and uses handler as the protocol's new handler which - * sends a String as a response. - */ - interceptStringProtocol(scheme: string, handler: (request: InterceptStringProtocolRequest, callback: (data?: string) => void) => void, completion?: (error: Error) => void): void; - /** - * The callback will be called with a boolean that indicates whether there is - * already a handler for scheme. Deprecated Soon - */ - isProtocolHandled(scheme: string, callback: (handled: boolean) => void): void; - isProtocolHandled(scheme: string): Promise; - /** - * Registers a protocol of scheme that will send a Buffer as a response. The usage - * is the same with registerFileProtocol, except that the callback should be called - * with either a Buffer object or an object that has the data, mimeType, and - * charset properties. Example: - */ - registerBufferProtocol(scheme: string, handler: (request: RegisterBufferProtocolRequest, callback: (buffer?: (Buffer) | (MimeTypedBuffer)) => void) => void, completion?: (error: Error) => void): void; - /** - * Registers a protocol of scheme that will send the file as a response. The - * handler will be called with handler(request, callback) when a request is going - * to be created with scheme. completion will be called with completion(null) when - * scheme is successfully registered or completion(error) when failed. To handle - * the request, the callback should be called with either the file's path or an - * object that has a path property, e.g. callback(filePath) or callback({ path: - * filePath }). The object may also have a headers property which gives a map of - * headers to values for the response headers, e.g. callback({ path: filePath, - * headers: {"Content-Security-Policy": "default-src 'none'"]}). When callback is - * called with nothing, a number, or an object that has an error property, the - * request will fail with the error number you specified. For the available error - * numbers you can use, please see the net error list. By default the scheme is - * treated like http:, which is parsed differently than protocols that follow the - * "generic URI syntax" like file:. - */ - registerFileProtocol(scheme: string, handler: (request: RegisterFileProtocolRequest, callback: (filePath?: string) => void) => void, completion?: (error: Error) => void): void; - /** - * Registers a protocol of scheme that will send an HTTP request as a response. The - * usage is the same with registerFileProtocol, except that the callback should be - * called with a redirectRequest object that has the url, method, referrer, - * uploadData and session properties. By default the HTTP request will reuse the - * current session. If you want the request to have a different session you should - * set session to null. For POST requests the uploadData object must be provided. - */ - registerHttpProtocol(scheme: string, handler: (request: RegisterHttpProtocolRequest, callback: (redirectRequest: RedirectRequest) => void) => void, completion?: (error: Error) => void): void; - /** - * Note: This method can only be used before the ready event of the app module gets - * emitted and can be called only once. Registers the scheme as standard, secure, - * bypasses content security policy for resources, allows registering ServiceWorker - * and supports fetch API. Specify a privilege with the value of true to enable the - * capability. An example of registering a privileged scheme, with bypassing - * Content Security Policy: A standard scheme adheres to what RFC 3986 calls - * generic URI syntax. For example http and https are standard schemes, while file - * is not. Registering a scheme as standard, will allow relative and absolute - * resources to be resolved correctly when served. Otherwise the scheme will behave - * like the file protocol, but without the ability to resolve relative URLs. For - * example when you load following page with custom protocol without registering it - * as standard scheme, the image will not be loaded because non-standard schemes - * can not recognize relative URLs: Registering a scheme as standard will allow - * access to files through the FileSystem API. Otherwise the renderer will throw a - * security error for the scheme. By default web storage apis (localStorage, - * sessionStorage, webSQL, indexedDB, cookies) are disabled for non standard - * schemes. So in general if you want to register a custom protocol to replace the - * http protocol, you have to register it as a standard scheme. - * protocol.registerSchemesAsPrivileged can be used to replicate the functionality - * of the previous protocol.registerStandardSchemes, webFrame.registerURLSchemeAs* - * and protocol.registerServiceWorkerSchemes functions that existed prior to - * Electron 5.0.0, for example: before (<= v4.x) after (>= v5.x) - */ - registerSchemesAsPrivileged(customSchemes: CustomScheme[]): void; - /** - * Registers a protocol of scheme that will send a Readable as a response. The - * usage is similar to the other register{Any}Protocol, except that the callback - * should be called with either a Readable object or an object that has the data, - * statusCode, and headers properties. Example: It is possible to pass any object - * that implements the readable stream API (emits data/end/error events). For - * example, here's how a file could be returned: - */ - registerStreamProtocol(scheme: string, handler: (request: RegisterStreamProtocolRequest, callback: (stream?: (NodeJS.ReadableStream) | (StreamProtocolResponse)) => void) => void, completion?: (error: Error) => void): void; - /** - * Registers a protocol of scheme that will send a String as a response. The usage - * is the same with registerFileProtocol, except that the callback should be called - * with either a String or an object that has the data, mimeType, and charset - * properties. - */ - registerStringProtocol(scheme: string, handler: (request: RegisterStringProtocolRequest, callback: (data?: string) => void) => void, completion?: (error: Error) => void): void; - /** - * Remove the interceptor installed for scheme and restore its original handler. - */ - uninterceptProtocol(scheme: string, completion?: (error: Error) => void): void; - /** - * Unregisters the custom protocol of scheme. - */ - unregisterProtocol(scheme: string, completion?: (error: Error) => void): void; - } - - interface Rectangle { - - // Docs: http://electronjs.org/docs/api/structures/rectangle - - /** - * The height of the rectangle (must be an integer). - */ - height: number; - /** - * The width of the rectangle (must be an integer). - */ - width: number; - /** - * The x coordinate of the origin of the rectangle (must be an integer). - */ - x: number; - /** - * The y coordinate of the origin of the rectangle (must be an integer). - */ - y: number; - } - - interface Referrer { - - // Docs: http://electronjs.org/docs/api/structures/referrer - - /** - * Can be default, unsafe-url, no-referrer-when-downgrade, no-referrer, origin, - * strict-origin-when-cross-origin, same-origin or strict-origin. See the for more - * details on the meaning of these values. - */ - policy: ('default' | 'unsafe-url' | 'no-referrer-when-downgrade' | 'no-referrer' | 'origin' | 'strict-origin-when-cross-origin' | 'same-origin' | 'strict-origin'); - /** - * HTTP Referrer URL. - */ - url: string; - } - - interface Remote extends MainInterface { - - // Docs: http://electronjs.org/docs/api/remote - - getCurrentWebContents(): WebContents; - /** - * Note: Do not use removeAllListeners on BrowserWindow. Use of this can remove all - * blur listeners, disable click events on touch bar buttons, and other unintended - * consequences. - */ - getCurrentWindow(): BrowserWindow; - getGlobal(name: string): any; - /** - * e.g. - */ - require(module: string): any; - /** - * The process object in the main process. This is the same as - * remote.getGlobal('process') but is cached. - */ - process?: any; - } - - interface RemoveClientCertificate { - - // Docs: http://electronjs.org/docs/api/structures/remove-client-certificate - - /** - * Origin of the server whose associated client certificate must be removed from - * the cache. - */ - origin: string; - /** - * clientCertificate. - */ - type: string; - } - - interface RemovePassword { - - // Docs: http://electronjs.org/docs/api/structures/remove-password - - /** - * When provided, the authentication info related to the origin will only be - * removed otherwise the entire cache will be cleared. - */ - origin?: string; - /** - * Credentials of the authentication. Must be provided if removing by origin. - */ - password?: string; - /** - * Realm of the authentication. Must be provided if removing by origin. - */ - realm?: string; - /** - * Scheme of the authentication. Can be basic, digest, ntlm, negotiate. Must be - * provided if removing by origin. - */ - scheme?: ('basic' | 'digest' | 'ntlm' | 'negotiate'); - /** - * password. - */ - type: string; - /** - * Credentials of the authentication. Must be provided if removing by origin. - */ - username?: string; - } - - interface Screen extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/screen - - /** - * Emitted when newDisplay has been added. - */ - on(event: 'display-added', listener: (event: Event, - newDisplay: Display) => void): this; - once(event: 'display-added', listener: (event: Event, - newDisplay: Display) => void): this; - addListener(event: 'display-added', listener: (event: Event, - newDisplay: Display) => void): this; - removeListener(event: 'display-added', listener: (event: Event, - newDisplay: Display) => void): this; - /** - * Emitted when one or more metrics change in a display. The changedMetrics is an - * array of strings that describe the changes. Possible changes are bounds, - * workArea, scaleFactor and rotation. - */ - on(event: 'display-metrics-changed', listener: (event: Event, - display: Display, - changedMetrics: string[]) => void): this; - once(event: 'display-metrics-changed', listener: (event: Event, - display: Display, - changedMetrics: string[]) => void): this; - addListener(event: 'display-metrics-changed', listener: (event: Event, - display: Display, - changedMetrics: string[]) => void): this; - removeListener(event: 'display-metrics-changed', listener: (event: Event, - display: Display, - changedMetrics: string[]) => void): this; - /** - * Emitted when oldDisplay has been removed. - */ - on(event: 'display-removed', listener: (event: Event, - oldDisplay: Display) => void): this; - once(event: 'display-removed', listener: (event: Event, - oldDisplay: Display) => void): this; - addListener(event: 'display-removed', listener: (event: Event, - oldDisplay: Display) => void): this; - removeListener(event: 'display-removed', listener: (event: Event, - oldDisplay: Display) => void): this; - /** - * Converts a screen DIP point to a screen physical point. The DPI scale is - * performed relative to the display containing the DIP point. - */ - dipToScreenPoint(point: Point): Point; - /** - * Converts a screen DIP rect to a screen physical rect. The DPI scale is performed - * relative to the display nearest to window. If window is null, scaling will be - * performed to the display nearest to rect. - */ - dipToScreenRect(window: (BrowserWindow) | (null), rect: Rectangle): Rectangle; - getAllDisplays(): Display[]; - /** - * The current absolute position of the mouse pointer. - */ - getCursorScreenPoint(): Point; - getDisplayMatching(rect: Rectangle): Display; - getDisplayNearestPoint(point: Point): Display; - getPrimaryDisplay(): Display; - /** - * Converts a screen physical point to a screen DIP point. The DPI scale is - * performed relative to the display containing the physical point. - */ - screenToDipPoint(point: Point): Point; - /** - * Converts a screen physical rect to a screen DIP rect. The DPI scale is performed - * relative to the display nearest to window. If window is null, scaling will be - * performed to the display nearest to rect. - */ - screenToDipRect(window: (BrowserWindow) | (null), rect: Rectangle): Rectangle; - } - - interface ScrubberItem { - - // Docs: http://electronjs.org/docs/api/structures/scrubber-item - - /** - * The image to appear in this item. - */ - icon?: NativeImage; - /** - * The text to appear in this item. - */ - label?: string; - } - - interface SegmentedControlSegment { - - // Docs: http://electronjs.org/docs/api/structures/segmented-control-segment - - /** - * Whether this segment is selectable. Default: true. - */ - enabled?: boolean; - /** - * The image to appear in this segment. - */ - icon?: NativeImage; - /** - * The text to appear in this segment. - */ - label?: string; - } - - class Session extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/session - - /** - * If partition starts with persist:, the page will use a persistent session - * available to all pages in the app with the same partition. if there is no - * persist: prefix, the page will use an in-memory session. If the partition is - * empty then default session of the app will be returned. To create a Session with - * options, you have to ensure the Session with the partition has never been used - * before. There is no way to change the options of an existing Session object. - */ - static fromPartition(partition: string, options?: FromPartitionOptions): Session; - /** - * A Session object, the default session object of the app. - */ - static defaultSession?: Session; - /** - * Emitted when Electron is about to download item in webContents. Calling - * event.preventDefault() will cancel the download and item will not be available - * from next tick of the process. - */ - on(event: 'will-download', listener: (event: Event, - item: DownloadItem, - webContents: WebContents) => void): this; - once(event: 'will-download', listener: (event: Event, - item: DownloadItem, - webContents: WebContents) => void): this; - addListener(event: 'will-download', listener: (event: Event, - item: DownloadItem, - webContents: WebContents) => void): this; - removeListener(event: 'will-download', listener: (event: Event, - item: DownloadItem, - webContents: WebContents) => void): this; - /** - * Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate - * authentication. - */ - allowNTLMCredentialsForDomains(domains: string): void; - clearAuthCache(): Promise; - clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate)): Promise; - /** - * Clears the session’s HTTP authentication cache. Deprecated Soon - */ - clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate), callback: Function): void; - /** - * Clears the session’s HTTP cache. - */ - clearCache(): Promise; - /** - * Clears the session’s HTTP cache. Deprecated Soon - */ - clearCache(callback: (error: number) => void): void; - /** - * Clears the host resolver cache. - */ - clearHostResolverCache(): Promise; - /** - * Clears the host resolver cache. Deprecated Soon - */ - clearHostResolverCache(callback?: Function): void; - /** - * Clears the storage data for the current session. Deprecated Soon - */ - clearStorageData(options?: ClearStorageDataOptions, callback?: Function): void; - clearStorageData(options?: ClearStorageDataOptions): Promise; - /** - * Allows resuming cancelled or interrupted downloads from previous Session. The - * API will generate a DownloadItem that can be accessed with the will-download - * event. The DownloadItem will not have any WebContents associated with it and the - * initial state will be interrupted. The download will start only when the resume - * API is called on the DownloadItem. - */ - createInterruptedDownload(options: CreateInterruptedDownloadOptions): void; - /** - * Disables any network emulation already active for the session. Resets to the - * original network configuration. - */ - disableNetworkEmulation(): void; - /** - * Emulates network with the given configuration for the session. - */ - enableNetworkEmulation(options: EnableNetworkEmulationOptions): void; - /** - * Writes any unwritten DOMStorage data to disk. - */ - flushStorageData(): void; - /** - * Deprecated Soon - */ - getBlobData(identifier: string, callback: (result: Buffer) => void): void; - getBlobData(identifier: string): Promise; - getCacheSize(): Promise; - /** - * Callback is invoked with the session's current cache size. Deprecated Soon - */ - getCacheSize(callback: (size: number, error: number) => void): void; - getPreloads(): string[]; - getUserAgent(): string; - resolveProxy(url: string): Promise; - /** - * Resolves the proxy information for url. The callback will be called with - * callback(proxy) when the request is performed. Deprecated Soon - */ - resolveProxy(url: string, callback: (proxy: string) => void): void; - /** - * Sets the certificate verify proc for session, the proc will be called with - * proc(request, callback) whenever a server certificate verification is requested. - * Calling callback(0) accepts the certificate, calling callback(-2) rejects it. - * Calling setCertificateVerifyProc(null) will revert back to default certificate - * verify proc. - */ - setCertificateVerifyProc(proc: (request: CertificateVerifyProcRequest, callback: (verificationResult: number) => void) => void): void; - /** - * Sets download saving directory. By default, the download directory will be the - * Downloads under the respective app folder. - */ - setDownloadPath(path: string): void; - /** - * Sets the handler which can be used to respond to permission checks for the - * session. Returning true will allow the permission and false will reject it. To - * clear the handler, call setPermissionCheckHandler(null). - */ - setPermissionCheckHandler(handler: ((webContents: WebContents, permission: string, requestingOrigin: string, details: PermissionCheckHandlerDetails) => boolean) | (null)): void; - /** - * Sets the handler which can be used to respond to permission requests for the - * session. Calling callback(true) will allow the permission and callback(false) - * will reject it. To clear the handler, call setPermissionRequestHandler(null). - */ - setPermissionRequestHandler(handler: ((webContents: WebContents, permission: string, callback: (permissionGranted: boolean) => void, details: PermissionRequestHandlerDetails) => void) | (null)): void; - /** - * Adds scripts that will be executed on ALL web contents that are associated with - * this session just before normal preload scripts run. - */ - setPreloads(preloads: string[]): void; - /** - * Sets the proxy settings. When pacScript and proxyRules are provided together, - * the proxyRules option is ignored and pacScript configuration is applied. The - * proxyRules has to follow the rules below: For example: The proxyBypassRules is a - * comma separated list of rules described below: - */ - setProxy(config: Config): Promise; - /** - * Sets the proxy settings. When pacScript and proxyRules are provided together, - * the proxyRules option is ignored and pacScript configuration is applied. The - * proxyRules has to follow the rules below: For example: The proxyBypassRules is a - * comma separated list of rules described below: Deprecated Soon - */ - setProxy(config: Config, callback: Function): void; - /** - * Overrides the userAgent and acceptLanguages for this session. The - * acceptLanguages must a comma separated ordered list of language codes, for - * example "en-US,fr,de,ko,zh-CN,ja". This doesn't affect existing WebContents, and - * each WebContents can use webContents.setUserAgent to override the session-wide - * user agent. - */ - setUserAgent(userAgent: string, acceptLanguages?: string): void; - cookies: Cookies; - netLog: NetLog; - protocol: Protocol; - webRequest: WebRequest; - } - - interface Shell { - - // Docs: http://electronjs.org/docs/api/shell - - /** - * Play the beep sound. - */ - beep(): void; - /** - * Move the given file to trash and returns a boolean status for the operation. - */ - moveItemToTrash(fullPath: string): boolean; - /** - * Open the given external protocol URL in the desktop's default manner. (For - * example, mailto: URLs in the user's default mail agent). - */ - openExternal(url: string, options?: OpenExternalOptions): Promise; - /** - * Open the given external protocol URL in the desktop's default manner. (For - * example, mailto: URLs in the user's default mail agent). Deprecated - */ - openExternalSync(url: string, options?: OpenExternalSyncOptions): boolean; - /** - * Open the given file in the desktop's default manner. - */ - openItem(fullPath: string): boolean; - /** - * Resolves the shortcut link at shortcutPath. An exception will be thrown when any - * error happens. - */ - readShortcutLink(shortcutPath: string): ShortcutDetails; - /** - * Show the given file in a file manager. If possible, select the file. - */ - showItemInFolder(fullPath: string): void; - /** - * Creates or updates a shortcut link at shortcutPath. - */ - writeShortcutLink(shortcutPath: string, operation: 'create' | 'update' | 'replace', options: ShortcutDetails): boolean; - /** - * Creates or updates a shortcut link at shortcutPath. - */ - writeShortcutLink(shortcutPath: string, options: ShortcutDetails): boolean; - } - - interface ShortcutDetails { - - // Docs: http://electronjs.org/docs/api/structures/shortcut-details - - /** - * The Application User Model ID. Default is empty. - */ - appUserModelId?: string; - /** - * The arguments to be applied to target when launching from this shortcut. Default - * is empty. - */ - args?: string; - /** - * The working directory. Default is empty. - */ - cwd?: string; - /** - * The description of the shortcut. Default is empty. - */ - description?: string; - /** - * The path to the icon, can be a DLL or EXE. icon and iconIndex have to be set - * together. Default is empty, which uses the target's icon. - */ - icon?: string; - /** - * The resource ID of icon when icon is a DLL or EXE. Default is 0. - */ - iconIndex?: number; - /** - * The target to launch from this shortcut. - */ - target: string; - } - - interface Size { - - // Docs: http://electronjs.org/docs/api/structures/size - - height: number; - width: number; - } - - interface StreamProtocolResponse { - - // Docs: http://electronjs.org/docs/api/structures/stream-protocol-response - - /** - * A Node.js readable stream representing the response body. - */ - data: NodeJS.ReadableStream; - /** - * An object containing the response headers. - */ - headers: Headers; - /** - * The HTTP response code. - */ - statusCode: number; - } - - interface SystemPreferences extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/system-preferences - - on(event: 'accent-color-changed', listener: (event: Event, - /** - * The new RGBA color the user assigned to be their system accent color. - */ - newColor: string) => void): this; - once(event: 'accent-color-changed', listener: (event: Event, - /** - * The new RGBA color the user assigned to be their system accent color. - */ - newColor: string) => void): this; - addListener(event: 'accent-color-changed', listener: (event: Event, - /** - * The new RGBA color the user assigned to be their system accent color. - */ - newColor: string) => void): this; - removeListener(event: 'accent-color-changed', listener: (event: Event, - /** - * The new RGBA color the user assigned to be their system accent color. - */ - newColor: string) => void): this; - on(event: 'color-changed', listener: (event: Event) => void): this; - once(event: 'color-changed', listener: (event: Event) => void): this; - addListener(event: 'color-changed', listener: (event: Event) => void): this; - removeListener(event: 'color-changed', listener: (event: Event) => void): this; - on(event: 'high-contrast-color-scheme-changed', listener: (event: Event, - /** - * `true` if a high contrast theme is being used, `false` otherwise. - */ - highContrastColorScheme: boolean) => void): this; - once(event: 'high-contrast-color-scheme-changed', listener: (event: Event, - /** - * `true` if a high contrast theme is being used, `false` otherwise. - */ - highContrastColorScheme: boolean) => void): this; - addListener(event: 'high-contrast-color-scheme-changed', listener: (event: Event, - /** - * `true` if a high contrast theme is being used, `false` otherwise. - */ - highContrastColorScheme: boolean) => void): this; - removeListener(event: 'high-contrast-color-scheme-changed', listener: (event: Event, - /** - * `true` if a high contrast theme is being used, `false` otherwise. - */ - highContrastColorScheme: boolean) => void): this; - on(event: 'inverted-color-scheme-changed', listener: (event: Event, - /** - * `true` if an inverted color scheme (a high contrast color scheme with light text - * and dark backgrounds) is being used, `false` otherwise. - */ - invertedColorScheme: boolean) => void): this; - once(event: 'inverted-color-scheme-changed', listener: (event: Event, - /** - * `true` if an inverted color scheme (a high contrast color scheme with light text - * and dark backgrounds) is being used, `false` otherwise. - */ - invertedColorScheme: boolean) => void): this; - addListener(event: 'inverted-color-scheme-changed', listener: (event: Event, - /** - * `true` if an inverted color scheme (a high contrast color scheme with light text - * and dark backgrounds) is being used, `false` otherwise. - */ - invertedColorScheme: boolean) => void): this; - removeListener(event: 'inverted-color-scheme-changed', listener: (event: Event, - /** - * `true` if an inverted color scheme (a high contrast color scheme with light text - * and dark backgrounds) is being used, `false` otherwise. - */ - invertedColorScheme: boolean) => void): this; - /** - * Important: In order to properly leverage this API, you must set the - * NSMicrophoneUsageDescription and NSCameraUsageDescription strings in your app's - * Info.plist file. The values for these keys will be used to populate the - * permission dialogs so that the user will be properly informed as to the purpose - * of the permission request. See Electron Application Distribution for more - * information about how to set these in the context of Electron. This user consent - * was not required until macOS 10.14 Mojave, so this method will always return - * true if your system is running 10.13 High Sierra or lower. - */ - askForMediaAccess(mediaType: 'microphone' | 'camera'): Promise; - /** - * NOTE: This API will return false on macOS systems older than Sierra 10.12.2. - */ - canPromptTouchID(): boolean; - /** - * This API is only available on macOS 10.14 Mojave or newer. - */ - getAccentColor(): string; - /** - * Returns an object with system animation settings. - */ - getAnimationSettings(): AnimationSettings; - /** - * Gets the macOS appearance setting that you have declared you want for your - * application, maps to NSApplication.appearance. You can use the - * setAppLevelAppearance API to set this value. - */ - getAppLevelAppearance(): ('dark' | 'light' | 'unknown'); - getColor(color: '3d-dark-shadow' | '3d-dark-shadow' | '3d-face' | '3d-highlight' | '3d-light' | '3d-shadow' | 'active-border' | 'active-caption' | 'active-caption-gradient' | 'app-workspace' | 'button-text' | 'caption-text' | 'desktop' | 'disabled-text' | 'highlight' | 'highlight-text' | 'hotlight' | 'inactive-border' | 'inactive-caption' | 'inactive-caption-gradient' | 'inactive-caption-text' | 'info-background' | 'info-text' | 'menu' | 'menu-highlight' | 'menubar' | 'menu-text' | 'scrollbar' | 'window' | 'window-frame' | 'window-text' | 'alternate-selected-control-text' | 'alternate-selected-control-text' | 'control-background' | 'control' | 'control-text' | 'disabled-control-text' | 'find-highlight' | 'grid' | 'header-text' | 'highlight' | 'keyboard-focus-indicator' | 'label' | 'link' | 'placeholder-text' | 'quaternary-label' | 'scrubber-textured-background' | 'secondary-label' | 'selected-content-background' | 'selected-control' | 'selected-control-text' | 'selected-menu-item' | 'selected-text-background' | 'selected-text' | 'separator' | 'shadow' | 'tertiary-label' | 'text-background' | 'text' | 'under-page-background' | 'unemphasized-selected-content-background' | 'unemphasized-selected-text-background' | 'unemphasized-selected-text' | 'window-background' | 'window-frame-text'): string; - /** - * Gets the macOS appearance setting that is currently applied to your application, - * maps to NSApplication.effectiveAppearance Please note that until Electron is - * built targeting the 10.14 SDK, your application's effectiveAppearance will - * default to 'light' and won't inherit the OS preference. In the interim in order - * for your application to inherit the OS preference you must set the - * NSRequiresAquaSystemAppearance key in your apps Info.plist to false. If you are - * using electron-packager or electron-forge just set the enableDarwinDarkMode - * packager option to true. See the Electron Packager API for more details. - */ - getEffectiveAppearance(): ('dark' | 'light' | 'unknown'); - /** - * This user consent was not required until macOS 10.14 Mojave, so this method will - * always return granted if your system is running 10.13 High Sierra or lower. - */ - getMediaAccessStatus(mediaType: string): ('not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'); - /** - * Returns one of several standard system colors that automatically adapt to - * vibrancy and changes in accessibility settings like 'Increase contrast' and - * 'Reduce transparency'. See Apple Documentation for more details. - */ - getSystemColor(color: 'blue' | 'brown' | 'gray' | 'green' | 'orange' | 'pink' | 'purple' | 'red' | 'yellow'): void; - /** - * Some popular key and types are: - */ - getUserDefault(key: string, type: 'string' | 'boolean' | 'integer' | 'float' | 'double' | 'url' | 'array' | 'dictionary'): any; - /** - * An example of using it to determine if you should create a transparent window or - * not (transparent windows won't work correctly when DWM composition is disabled): - */ - isAeroGlassEnabled(): boolean; - isDarkMode(): boolean; - isHighContrastColorScheme(): boolean; - isInvertedColorScheme(): boolean; - isSwipeTrackingFromScrollEventsEnabled(): boolean; - isTrustedAccessibilityClient(prompt: boolean): boolean; - /** - * Posts event as native notifications of macOS. The userInfo is an Object that - * contains the user information dictionary sent along with the notification. - */ - postLocalNotification(event: string, userInfo: any): void; - /** - * Posts event as native notifications of macOS. The userInfo is an Object that - * contains the user information dictionary sent along with the notification. - */ - postNotification(event: string, userInfo: any, deliverImmediately?: boolean): void; - /** - * Posts event as native notifications of macOS. The userInfo is an Object that - * contains the user information dictionary sent along with the notification. - */ - postWorkspaceNotification(event: string, userInfo: any): void; - /** - * This API itself will not protect your user data; rather, it is a mechanism to - * allow you to do so. Native apps will need to set Access Control Constants like - * kSecAccessControlUserPresence on the their keychain entry so that reading it - * would auto-prompt for Touch ID biometric consent. This could be done with - * node-keytar, such that one would store an encryption key with node-keytar and - * only fetch it if promptTouchID() resolves. NOTE: This API will return a rejected - * Promise on macOS systems older than Sierra 10.12.2. - */ - promptTouchID(reason: string): Promise; - /** - * Add the specified defaults to your application's NSUserDefaults. - */ - registerDefaults(defaults: any): void; - /** - * Removes the key in NSUserDefaults. This can be used to restore the default or - * global value of a key previously set with setUserDefault. - */ - removeUserDefault(key: string): void; - /** - * Sets the appearance setting for your application, this should override the - * system default and override the value of getEffectiveAppearance. - */ - setAppLevelAppearance(appearance: 'dark' | 'light'): void; - /** - * Set the value of key in NSUserDefaults. Note that type should match actual type - * of value. An exception is thrown if they don't. Some popular key and types are: - */ - setUserDefault(key: string, type: string, value: string): void; - /** - * Same as subscribeNotification, but uses NSNotificationCenter for local defaults. - * This is necessary for events such as NSUserDefaultsDidChangeNotification. - */ - subscribeLocalNotification(event: string, callback: (event: string, userInfo: any) => void): number; - /** - * Subscribes to native notifications of macOS, callback will be called with - * callback(event, userInfo) when the corresponding event happens. The userInfo is - * an Object that contains the user information dictionary sent along with the - * notification. The id of the subscriber is returned, which can be used to - * unsubscribe the event. Under the hood this API subscribes to - * NSDistributedNotificationCenter, example values of event are: - */ - subscribeNotification(event: string, callback: (event: string, userInfo: any) => void): number; - /** - * Same as subscribeNotification, but uses - * NSWorkspace.sharedWorkspace.notificationCenter. This is necessary for events - * such as NSWorkspaceDidActivateApplicationNotification. - */ - subscribeWorkspaceNotification(event: string, callback: (event: string, userInfo: any) => void): void; - /** - * Same as unsubscribeNotification, but removes the subscriber from - * NSNotificationCenter. - */ - unsubscribeLocalNotification(id: number): void; - /** - * Removes the subscriber with id. - */ - unsubscribeNotification(id: number): void; - /** - * Same as unsubscribeNotification, but removes the subscriber from - * NSWorkspace.sharedWorkspace.notificationCenter. - */ - unsubscribeWorkspaceNotification(id: number): void; - } - - interface Task { - - // Docs: http://electronjs.org/docs/api/structures/task - - /** - * The command line arguments when program is executed. - */ - arguments: string; - /** - * Description of this task. - */ - description: string; - /** - * The icon index in the icon file. If an icon file consists of two or more icons, - * set this value to identify the icon. If an icon file consists of one icon, this - * value is 0. - */ - iconIndex: number; - /** - * The absolute path to an icon to be displayed in a JumpList, which can be an - * arbitrary resource file that contains an icon. You can usually specify - * process.execPath to show the icon of the program. - */ - iconPath: string; - /** - * Path of the program to execute, usually you should specify process.execPath - * which opens the current program. - */ - program: string; - /** - * The string to be displayed in a JumpList. - */ - title: string; - /** - * The working directory. Default is empty. - */ - workingDirectory?: string; - } - - interface ThumbarButton { - - // Docs: http://electronjs.org/docs/api/structures/thumbar-button - - click: Function; - /** - * Control specific states and behaviors of the button. By default, it is - * ['enabled']. - */ - flags?: string[]; - /** - * The icon showing in thumbnail toolbar. - */ - icon: NativeImage; - /** - * The text of the button's tooltip. - */ - tooltip?: string; - } - - class TouchBarButton extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-button - - constructor(options: TouchBarButtonConstructorOptions); - backgroundColor: string; - icon: NativeImage; - label: string; - } - - class TouchBarColorPicker extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-color-picker - - constructor(options: TouchBarColorPickerConstructorOptions); - availableColors: string[]; - selectedColor: string; - } - - class TouchBarGroup extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-group - - constructor(options: TouchBarGroupConstructorOptions); - } - - class TouchBarLabel extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-label - - constructor(options: TouchBarLabelConstructorOptions); - label: string; - textColor: string; - } - - class TouchBarPopover extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-popover - - constructor(options: TouchBarPopoverConstructorOptions); - icon: NativeImage; - label: string; - } - - class TouchBarScrubber extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-scrubber - - constructor(options: TouchBarScrubberConstructorOptions); - continuous: boolean; - items: ScrubberItem[]; - mode: string; - overlayStyle: string; - selectedStyle: string; - showArrowButtons: boolean; - } - - class TouchBarSegmentedControl extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-segmented-control - - constructor(options: TouchBarSegmentedControlConstructorOptions); - segments: SegmentedControlSegment[]; - segmentStyle: string; - selectedIndex: number; - } - - class TouchBarSlider extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-slider - - constructor(options: TouchBarSliderConstructorOptions); - label: string; - maxValue: number; - minValue: number; - value: number; - } - - class TouchBarSpacer extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-spacer - - constructor(options: TouchBarSpacerConstructorOptions); - } - - class TouchBar extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar - - constructor(options: TouchBarConstructorOptions); - escapeItem: (TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer | null); - static TouchBarButton: typeof TouchBarButton; - static TouchBarColorPicker: typeof TouchBarColorPicker; - static TouchBarGroup: typeof TouchBarGroup; - static TouchBarLabel: typeof TouchBarLabel; - static TouchBarPopover: typeof TouchBarPopover; - static TouchBarScrubber: typeof TouchBarScrubber; - static TouchBarSegmentedControl: typeof TouchBarSegmentedControl; - static TouchBarSlider: typeof TouchBarSlider; - static TouchBarSpacer: typeof TouchBarSpacer; - } - - interface TraceCategoriesAndOptions { - - // Docs: http://electronjs.org/docs/api/structures/trace-categories-and-options - - /** - * – is a filter to control what category groups should be traced. A filter can - * have an optional prefix to exclude category groups that contain a matching - * category. Having both included and excluded category patterns in the same list - * is not supported. Examples: test_MyTest*, test_MyTest*,test_OtherStuff, - * -excluded_category1,-excluded_category2. - */ - categoryFilter: string; - /** - * Controls what kind of tracing is enabled, it is a comma-delimited sequence of - * the following strings: record-until-full, record-continuously, trace-to-console, - * enable-sampling, enable-systrace, e.g. 'record-until-full,enable-sampling'. The - * first 3 options are trace recording modes and hence mutually exclusive. If more - * than one trace recording modes appear in the traceOptions string, the last one - * takes precedence. If none of the trace recording modes are specified, recording - * mode is record-until-full. The trace option will first be reset to the default - * option (record_mode set to record-until-full, enable_sampling and - * enable_systrace set to false) before options parsed from traceOptions are - * applied on it. - */ - traceOptions: string; - } - - interface TraceConfig { - - // Docs: http://electronjs.org/docs/api/structures/trace-config - - excluded_categories?: string[]; - included_categories?: string[]; - memory_dump_config?: MemoryDumpConfig; - } - - interface Transaction { - - // Docs: http://electronjs.org/docs/api/structures/transaction - - /** - * The error code if an error occurred while processing the transaction. - */ - errorCode: number; - /** - * The error message if an error occurred while processing the transaction. - */ - errorMessage: string; - /** - * The identifier of the restored transaction by the App Store. - */ - originalTransactionIdentifier: string; - payment: Payment; - /** - * The date the transaction was added to the App Store’s payment queue. - */ - transactionDate: string; - /** - * A string that uniquely identifies a successful payment transaction. - */ - transactionIdentifier: string; - /** - * The transaction state, can be purchasing, purchased, failed, restored or - * deferred. - */ - transactionState: ('purchasing' | 'purchased' | 'failed' | 'restored' | 'deferred'); - } - - class Tray extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/tray - - /** - * Emitted when the tray balloon is clicked. - */ - on(event: 'balloon-click', listener: Function): this; - once(event: 'balloon-click', listener: Function): this; - addListener(event: 'balloon-click', listener: Function): this; - removeListener(event: 'balloon-click', listener: Function): this; - /** - * Emitted when the tray balloon is closed because of timeout or user manually - * closes it. - */ - on(event: 'balloon-closed', listener: Function): this; - once(event: 'balloon-closed', listener: Function): this; - addListener(event: 'balloon-closed', listener: Function): this; - removeListener(event: 'balloon-closed', listener: Function): this; - /** - * Emitted when the tray balloon shows. - */ - on(event: 'balloon-show', listener: Function): this; - once(event: 'balloon-show', listener: Function): this; - addListener(event: 'balloon-show', listener: Function): this; - removeListener(event: 'balloon-show', listener: Function): this; - /** - * Emitted when the tray icon is clicked. - */ - on(event: 'click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle, - /** - * The position of the event. - */ - position: Point) => void): this; - once(event: 'click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle, - /** - * The position of the event. - */ - position: Point) => void): this; - addListener(event: 'click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle, - /** - * The position of the event. - */ - position: Point) => void): this; - removeListener(event: 'click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle, - /** - * The position of the event. - */ - position: Point) => void): this; - /** - * Emitted when the tray icon is double clicked. - */ - on(event: 'double-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - once(event: 'double-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - addListener(event: 'double-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - removeListener(event: 'double-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - /** - * Emitted when a drag operation ends on the tray or ends at another location. - */ - on(event: 'drag-end', listener: Function): this; - once(event: 'drag-end', listener: Function): this; - addListener(event: 'drag-end', listener: Function): this; - removeListener(event: 'drag-end', listener: Function): this; - /** - * Emitted when a drag operation enters the tray icon. - */ - on(event: 'drag-enter', listener: Function): this; - once(event: 'drag-enter', listener: Function): this; - addListener(event: 'drag-enter', listener: Function): this; - removeListener(event: 'drag-enter', listener: Function): this; - /** - * Emitted when a drag operation exits the tray icon. - */ - on(event: 'drag-leave', listener: Function): this; - once(event: 'drag-leave', listener: Function): this; - addListener(event: 'drag-leave', listener: Function): this; - removeListener(event: 'drag-leave', listener: Function): this; - /** - * Emitted when any dragged items are dropped on the tray icon. - */ - on(event: 'drop', listener: Function): this; - once(event: 'drop', listener: Function): this; - addListener(event: 'drop', listener: Function): this; - removeListener(event: 'drop', listener: Function): this; - /** - * Emitted when dragged files are dropped in the tray icon. - */ - on(event: 'drop-files', listener: (event: Event, - /** - * The paths of the dropped files. - */ - files: string[]) => void): this; - once(event: 'drop-files', listener: (event: Event, - /** - * The paths of the dropped files. - */ - files: string[]) => void): this; - addListener(event: 'drop-files', listener: (event: Event, - /** - * The paths of the dropped files. - */ - files: string[]) => void): this; - removeListener(event: 'drop-files', listener: (event: Event, - /** - * The paths of the dropped files. - */ - files: string[]) => void): this; - /** - * Emitted when dragged text is dropped in the tray icon. - */ - on(event: 'drop-text', listener: (event: Event, - /** - * the dropped text string. - */ - text: string) => void): this; - once(event: 'drop-text', listener: (event: Event, - /** - * the dropped text string. - */ - text: string) => void): this; - addListener(event: 'drop-text', listener: (event: Event, - /** - * the dropped text string. - */ - text: string) => void): this; - removeListener(event: 'drop-text', listener: (event: Event, - /** - * the dropped text string. - */ - text: string) => void): this; - /** - * Emitted when the mouse enters the tray icon. - */ - on(event: 'mouse-enter', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - once(event: 'mouse-enter', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - addListener(event: 'mouse-enter', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - removeListener(event: 'mouse-enter', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - /** - * Emitted when the mouse exits the tray icon. - */ - on(event: 'mouse-leave', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - once(event: 'mouse-leave', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - addListener(event: 'mouse-leave', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - removeListener(event: 'mouse-leave', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - /** - * Emitted when the mouse moves in the tray icon. - */ - on(event: 'mouse-move', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - once(event: 'mouse-move', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - addListener(event: 'mouse-move', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - removeListener(event: 'mouse-move', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - /** - * Emitted when the tray icon is right clicked. - */ - on(event: 'right-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - once(event: 'right-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - addListener(event: 'right-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - removeListener(event: 'right-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - constructor(image: (NativeImage) | (string)); - /** - * Destroys the tray icon immediately. - */ - destroy(): void; - /** - * Displays a tray balloon. - */ - displayBalloon(options: DisplayBalloonOptions): void; - /** - * The bounds of this tray icon as Object. - */ - getBounds(): Rectangle; - getIgnoreDoubleClickEvents(): boolean; - getTitle(title: string): string; - isDestroyed(): boolean; - /** - * Pops up the context menu of the tray icon. When menu is passed, the menu will be - * shown instead of the tray icon's context menu. The position is only available on - * Windows, and it is (0, 0) by default. - */ - popUpContextMenu(menu?: Menu, position?: Point): void; - /** - * Sets the context menu for this icon. - */ - setContextMenu(menu: (Menu) | (null)): void; - /** - * Sets when the tray's icon background becomes highlighted (in blue). Deprecated - * Note: You can use highlightMode with a BrowserWindow by toggling between 'never' - * and 'always' modes when the window visibility changes. - */ - setHighlightMode(mode: 'selection' | 'always' | 'never'): void; - /** - * Sets the option to ignore double click events. Ignoring these events allows you - * to detect every individual click of the tray icon. This value is set to false by - * default. - */ - setIgnoreDoubleClickEvents(ignore: boolean): void; - /** - * Sets the image associated with this tray icon. - */ - setImage(image: (NativeImage) | (string)): void; - /** - * Sets the image associated with this tray icon when pressed on macOS. - */ - setPressedImage(image: (NativeImage) | (string)): void; - /** - * Sets the title displayed next to the tray icon in the status bar (Support ANSI - * colors). - */ - setTitle(title: string): void; - /** - * Sets the hover text for this tray icon. - */ - setToolTip(toolTip: string): void; - } - - interface UploadBlob { - - // Docs: http://electronjs.org/docs/api/structures/upload-blob - - /** - * UUID of blob data to upload. - */ - blobUUID: string; - /** - * blob. - */ - type: string; - } - - interface UploadData { - - // Docs: http://electronjs.org/docs/api/structures/upload-data - - /** - * UUID of blob data. Use method to retrieve the data. - */ - blobUUID: string; - /** - * Content being sent. - */ - bytes: Buffer; - /** - * Path of file being uploaded. - */ - file: string; - } - - interface UploadFile { - - // Docs: http://electronjs.org/docs/api/structures/upload-file - - /** - * Path of file to be uploaded. - */ - filePath: string; - /** - * Number of bytes to read from offset. Defaults to 0. - */ - length: number; - /** - * Last Modification time in number of seconds since the UNIX epoch. - */ - modificationTime: number; - /** - * Defaults to 0. - */ - offset: number; - /** - * file. - */ - type: string; - } - - interface UploadRawData { - - // Docs: http://electronjs.org/docs/api/structures/upload-raw-data - - /** - * Data to be uploaded. - */ - bytes: Buffer; - /** - * rawData. - */ - type: string; - } - - class WebContents extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/web-contents - - static fromId(id: number): WebContents; - static getAllWebContents(): WebContents[]; - static getFocusedWebContents(): WebContents; - /** - * Emitted before dispatching the keydown and keyup events in the page. Calling - * event.preventDefault will prevent the page keydown/keyup events and the menu - * shortcuts. To only prevent the menu shortcuts, use setIgnoreMenuShortcuts: - */ - on(event: 'before-input-event', listener: (event: Event, - /** - * Input properties. - */ - input: Input) => void): this; - once(event: 'before-input-event', listener: (event: Event, - /** - * Input properties. - */ - input: Input) => void): this; - addListener(event: 'before-input-event', listener: (event: Event, - /** - * Input properties. - */ - input: Input) => void): this; - removeListener(event: 'before-input-event', listener: (event: Event, - /** - * Input properties. - */ - input: Input) => void): this; - /** - * Emitted when failed to verify the certificate for url. The usage is the same - * with the certificate-error event of app. - */ - on(event: 'certificate-error', listener: (event: Event, - url: string, - /** - * The error code. - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - once(event: 'certificate-error', listener: (event: Event, - url: string, - /** - * The error code. - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - addListener(event: 'certificate-error', listener: (event: Event, - url: string, - /** - * The error code. - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - removeListener(event: 'certificate-error', listener: (event: Event, - url: string, - /** - * The error code. - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - /** - * Emitted when the associated window logs a console message. Will not be emitted - * for windows with offscreen rendering enabled. - */ - on(event: 'console-message', listener: (event: Event, - level: number, - message: string, - line: number, - sourceId: string) => void): this; - once(event: 'console-message', listener: (event: Event, - level: number, - message: string, - line: number, - sourceId: string) => void): this; - addListener(event: 'console-message', listener: (event: Event, - level: number, - message: string, - line: number, - sourceId: string) => void): this; - removeListener(event: 'console-message', listener: (event: Event, - level: number, - message: string, - line: number, - sourceId: string) => void): this; - /** - * Emitted when there is a new context menu that needs to be handled. - */ - on(event: 'context-menu', listener: (event: Event, - params: ContextMenuParams) => void): this; - once(event: 'context-menu', listener: (event: Event, - params: ContextMenuParams) => void): this; - addListener(event: 'context-menu', listener: (event: Event, - params: ContextMenuParams) => void): this; - removeListener(event: 'context-menu', listener: (event: Event, - params: ContextMenuParams) => void): this; - /** - * Emitted when the renderer process crashes or is killed. - */ - on(event: 'crashed', listener: (event: Event, - killed: boolean) => void): this; - once(event: 'crashed', listener: (event: Event, - killed: boolean) => void): this; - addListener(event: 'crashed', listener: (event: Event, - killed: boolean) => void): this; - removeListener(event: 'crashed', listener: (event: Event, - killed: boolean) => void): this; - /** - * Emitted when the cursor's type changes. The type parameter can be default, - * crosshair, pointer, text, wait, help, e-resize, n-resize, ne-resize, nw-resize, - * s-resize, se-resize, sw-resize, w-resize, ns-resize, ew-resize, nesw-resize, - * nwse-resize, col-resize, row-resize, m-panning, e-panning, n-panning, - * ne-panning, nw-panning, s-panning, se-panning, sw-panning, w-panning, move, - * vertical-text, cell, context-menu, alias, progress, nodrop, copy, none, - * not-allowed, zoom-in, zoom-out, grab, grabbing or custom. If the type parameter - * is custom, the image parameter will hold the custom cursor image in a - * NativeImage, and scale, size and hotspot will hold additional information about - * the custom cursor. - */ - on(event: 'cursor-changed', listener: (event: Event, - type: string, - image?: NativeImage, - /** - * scaling factor for the custom cursor. - */ - scale?: number, - /** - * the size of the `image`. - */ - size?: Size, - /** - * coordinates of the custom cursor's hotspot. - */ - hotspot?: Point) => void): this; - once(event: 'cursor-changed', listener: (event: Event, - type: string, - image?: NativeImage, - /** - * scaling factor for the custom cursor. - */ - scale?: number, - /** - * the size of the `image`. - */ - size?: Size, - /** - * coordinates of the custom cursor's hotspot. - */ - hotspot?: Point) => void): this; - addListener(event: 'cursor-changed', listener: (event: Event, - type: string, - image?: NativeImage, - /** - * scaling factor for the custom cursor. - */ - scale?: number, - /** - * the size of the `image`. - */ - size?: Size, - /** - * coordinates of the custom cursor's hotspot. - */ - hotspot?: Point) => void): this; - removeListener(event: 'cursor-changed', listener: (event: Event, - type: string, - image?: NativeImage, - /** - * scaling factor for the custom cursor. - */ - scale?: number, - /** - * the size of the `image`. - */ - size?: Size, - /** - * coordinates of the custom cursor's hotspot. - */ - hotspot?: Point) => void): this; - /** - * Emitted when desktopCapturer.getSources() is called in the renderer process. - * Calling event.preventDefault() will make it return empty sources. - */ - on(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; - once(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; - addListener(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; - removeListener(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; - /** - * Emitted when webContents is destroyed. - */ - on(event: 'destroyed', listener: Function): this; - once(event: 'destroyed', listener: Function): this; - addListener(event: 'destroyed', listener: Function): this; - removeListener(event: 'destroyed', listener: Function): this; - /** - * Emitted when DevTools is closed. - */ - on(event: 'devtools-closed', listener: Function): this; - once(event: 'devtools-closed', listener: Function): this; - addListener(event: 'devtools-closed', listener: Function): this; - removeListener(event: 'devtools-closed', listener: Function): this; - /** - * Emitted when DevTools is focused / opened. - */ - on(event: 'devtools-focused', listener: Function): this; - once(event: 'devtools-focused', listener: Function): this; - addListener(event: 'devtools-focused', listener: Function): this; - removeListener(event: 'devtools-focused', listener: Function): this; - /** - * Emitted when DevTools is opened. - */ - on(event: 'devtools-opened', listener: Function): this; - once(event: 'devtools-opened', listener: Function): this; - addListener(event: 'devtools-opened', listener: Function): this; - removeListener(event: 'devtools-opened', listener: Function): this; - /** - * Emitted when the devtools window instructs the webContents to reload - */ - on(event: 'devtools-reload-page', listener: Function): this; - once(event: 'devtools-reload-page', listener: Function): this; - addListener(event: 'devtools-reload-page', listener: Function): this; - removeListener(event: 'devtools-reload-page', listener: Function): this; - /** - * Emitted when a has been attached to this web contents. - */ - on(event: 'did-attach-webview', listener: (event: Event, - /** - * The guest web contents that is used by the ` - */ - webContents: WebContents) => void): this; - once(event: 'did-attach-webview', listener: (event: Event, - /** - * The guest web contents that is used by the ` - */ - webContents: WebContents) => void): this; - addListener(event: 'did-attach-webview', listener: (event: Event, - /** - * The guest web contents that is used by the ` - */ - webContents: WebContents) => void): this; - removeListener(event: 'did-attach-webview', listener: (event: Event, - /** - * The guest web contents that is used by the ` - */ - webContents: WebContents) => void): this; - /** - * Emitted when a page's theme color changes. This is usually due to encountering a - * meta tag: - */ - on(event: 'did-change-theme-color', listener: (event: Event, - /** - * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. - */ - color: (string) | (null)) => void): this; - once(event: 'did-change-theme-color', listener: (event: Event, - /** - * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. - */ - color: (string) | (null)) => void): this; - addListener(event: 'did-change-theme-color', listener: (event: Event, - /** - * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. - */ - color: (string) | (null)) => void): this; - removeListener(event: 'did-change-theme-color', listener: (event: Event, - /** - * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. - */ - color: (string) | (null)) => void): this; - /** - * This event is like did-finish-load but emitted when the load failed or was - * cancelled, e.g. window.stop() is invoked. The full list of error codes and their - * meaning is available here. - */ - on(event: 'did-fail-load', listener: (event: Event, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-fail-load', listener: (event: Event, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-fail-load', listener: (event: Event, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-fail-load', listener: (event: Event, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Emitted when the navigation is done, i.e. the spinner of the tab has stopped - * spinning, and the onload event was dispatched. - */ - on(event: 'did-finish-load', listener: Function): this; - once(event: 'did-finish-load', listener: Function): this; - addListener(event: 'did-finish-load', listener: Function): this; - removeListener(event: 'did-finish-load', listener: Function): this; - /** - * Emitted when a frame has done navigation. - */ - on(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Emitted when any frame navigation is done. This event is not emitted for in-page - * navigations, such as clicking anchor links or updating the window.location.hash. - * Use did-navigate-in-page event for this purpose. - */ - on(event: 'did-frame-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations, - */ - httpStatusText: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-frame-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations, - */ - httpStatusText: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-frame-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations, - */ - httpStatusText: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-frame-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations, - */ - httpStatusText: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Emitted when a main frame navigation is done. This event is not emitted for - * in-page navigations, such as clicking anchor links or updating the - * window.location.hash. Use did-navigate-in-page event for this purpose. - */ - on(event: 'did-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations - */ - httpStatusText: string) => void): this; - once(event: 'did-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations - */ - httpStatusText: string) => void): this; - addListener(event: 'did-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations - */ - httpStatusText: string) => void): this; - removeListener(event: 'did-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations - */ - httpStatusText: string) => void): this; - /** - * Emitted when an in-page navigation happened in any frame. When in-page - * navigation happens, the page URL changes but does not cause navigation outside - * of the page. Examples of this occurring are when anchor links are clicked or - * when the DOM hashchange event is triggered. - */ - on(event: 'did-navigate-in-page', listener: (event: Event, - url: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-navigate-in-page', listener: (event: Event, - url: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-navigate-in-page', listener: (event: Event, - url: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-navigate-in-page', listener: (event: Event, - url: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Emitted after a server side redirect occurs during navigation. For example a - * 302 redirect. This event can not be prevented, if you want to prevent redirects - * you should checkout out the will-redirect event above. - */ - on(event: 'did-redirect-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-redirect-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-redirect-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-redirect-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Corresponds to the points in time when the spinner of the tab started spinning. - */ - on(event: 'did-start-loading', listener: Function): this; - once(event: 'did-start-loading', listener: Function): this; - addListener(event: 'did-start-loading', listener: Function): this; - removeListener(event: 'did-start-loading', listener: Function): this; - /** - * Emitted when any frame (including main) starts navigating. isInplace will be - * true for in-page navigations. - */ - on(event: 'did-start-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-start-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-start-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-start-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Corresponds to the points in time when the spinner of the tab stopped spinning. - */ - on(event: 'did-stop-loading', listener: Function): this; - once(event: 'did-stop-loading', listener: Function): this; - addListener(event: 'did-stop-loading', listener: Function): this; - removeListener(event: 'did-stop-loading', listener: Function): this; - /** - * Emitted when the document in the given frame is loaded. - */ - on(event: 'dom-ready', listener: (event: Event) => void): this; - once(event: 'dom-ready', listener: (event: Event) => void): this; - addListener(event: 'dom-ready', listener: (event: Event) => void): this; - removeListener(event: 'dom-ready', listener: (event: Event) => void): this; - /** - * Emitted when the window enters a full-screen state triggered by HTML API. - */ - on(event: 'enter-html-full-screen', listener: Function): this; - once(event: 'enter-html-full-screen', listener: Function): this; - addListener(event: 'enter-html-full-screen', listener: Function): this; - removeListener(event: 'enter-html-full-screen', listener: Function): this; - /** - * Emitted when a result is available for [webContents.findInPage] request. - */ - on(event: 'found-in-page', listener: (event: Event, - result: Result) => void): this; - once(event: 'found-in-page', listener: (event: Event, - result: Result) => void): this; - addListener(event: 'found-in-page', listener: (event: Event, - result: Result) => void): this; - removeListener(event: 'found-in-page', listener: (event: Event, - result: Result) => void): this; - /** - * Emitted when the renderer process sends an asynchronous message via - * ipcRenderer.send(). - */ - on(event: 'ipc-message', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - once(event: 'ipc-message', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - addListener(event: 'ipc-message', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - removeListener(event: 'ipc-message', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - /** - * Emitted when the renderer process sends a synchronous message via - * ipcRenderer.sendSync(). - */ - on(event: 'ipc-message-sync', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - once(event: 'ipc-message-sync', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - addListener(event: 'ipc-message-sync', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - removeListener(event: 'ipc-message-sync', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - /** - * Emitted when the window leaves a full-screen state triggered by HTML API. - */ - on(event: 'leave-html-full-screen', listener: Function): this; - once(event: 'leave-html-full-screen', listener: Function): this; - addListener(event: 'leave-html-full-screen', listener: Function): this; - removeListener(event: 'leave-html-full-screen', listener: Function): this; - /** - * Emitted when webContents wants to do basic auth. The usage is the same with the - * login event of app. - */ - on(event: 'login', listener: (event: Event, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - once(event: 'login', listener: (event: Event, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - addListener(event: 'login', listener: (event: Event, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - removeListener(event: 'login', listener: (event: Event, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - /** - * Emitted when media is paused or done playing. - */ - on(event: 'media-paused', listener: Function): this; - once(event: 'media-paused', listener: Function): this; - addListener(event: 'media-paused', listener: Function): this; - removeListener(event: 'media-paused', listener: Function): this; - /** - * Emitted when media starts playing. - */ - on(event: 'media-started-playing', listener: Function): this; - once(event: 'media-started-playing', listener: Function): this; - addListener(event: 'media-started-playing', listener: Function): this; - removeListener(event: 'media-started-playing', listener: Function): this; - /** - * Emitted when the page requests to open a new window for a url. It could be - * requested by window.open or an external link like . By - * default a new BrowserWindow will be created for the url. Calling - * event.preventDefault() will prevent Electron from automatically creating a new - * BrowserWindow. If you call event.preventDefault() and manually create a new - * BrowserWindow then you must set event.newGuest to reference the new - * BrowserWindow instance, failing to do so may result in unexpected behavior. For - * example: - */ - on(event: 'new-window', listener: (event: Event, - url: string, - frameName: string, - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), - /** - * The options which will be used for creating the new . - */ - options: any, - /** - * The non-standard features (features not handled by Chromium or Electron) given - * to `window.open()`. - */ - additionalFeatures: string[], - /** - * The referrer that will be passed to the new window. May or may not result in the - * `Referer` header being sent, depending on the referrer policy. - */ - referrer: Referrer) => void): this; - once(event: 'new-window', listener: (event: Event, - url: string, - frameName: string, - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), - /** - * The options which will be used for creating the new . - */ - options: any, - /** - * The non-standard features (features not handled by Chromium or Electron) given - * to `window.open()`. - */ - additionalFeatures: string[], - /** - * The referrer that will be passed to the new window. May or may not result in the - * `Referer` header being sent, depending on the referrer policy. - */ - referrer: Referrer) => void): this; - addListener(event: 'new-window', listener: (event: Event, - url: string, - frameName: string, - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), - /** - * The options which will be used for creating the new . - */ - options: any, - /** - * The non-standard features (features not handled by Chromium or Electron) given - * to `window.open()`. - */ - additionalFeatures: string[], - /** - * The referrer that will be passed to the new window. May or may not result in the - * `Referer` header being sent, depending on the referrer policy. - */ - referrer: Referrer) => void): this; - removeListener(event: 'new-window', listener: (event: Event, - url: string, - frameName: string, - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), - /** - * The options which will be used for creating the new . - */ - options: any, - /** - * The non-standard features (features not handled by Chromium or Electron) given - * to `window.open()`. - */ - additionalFeatures: string[], - /** - * The referrer that will be passed to the new window. May or may not result in the - * `Referer` header being sent, depending on the referrer policy. - */ - referrer: Referrer) => void): this; - /** - * Emitted when page receives favicon urls. - */ - on(event: 'page-favicon-updated', listener: (event: Event, - /** - * Array of URLs. - */ - favicons: string[]) => void): this; - once(event: 'page-favicon-updated', listener: (event: Event, - /** - * Array of URLs. - */ - favicons: string[]) => void): this; - addListener(event: 'page-favicon-updated', listener: (event: Event, - /** - * Array of URLs. - */ - favicons: string[]) => void): this; - removeListener(event: 'page-favicon-updated', listener: (event: Event, - /** - * Array of URLs. - */ - favicons: string[]) => void): this; - /** - * Fired when page title is set during navigation. explicitSet is false when title - * is synthesized from file url. - */ - on(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - once(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - addListener(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - removeListener(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - /** - * Emitted when a new frame is generated. Only the dirty area is passed in the - * buffer. - */ - on(event: 'paint', listener: (event: Event, - dirtyRect: Rectangle, - /** - * The image data of the whole frame. - */ - image: NativeImage) => void): this; - once(event: 'paint', listener: (event: Event, - dirtyRect: Rectangle, - /** - * The image data of the whole frame. - */ - image: NativeImage) => void): this; - addListener(event: 'paint', listener: (event: Event, - dirtyRect: Rectangle, - /** - * The image data of the whole frame. - */ - image: NativeImage) => void): this; - removeListener(event: 'paint', listener: (event: Event, - dirtyRect: Rectangle, - /** - * The image data of the whole frame. - */ - image: NativeImage) => void): this; - /** - * Emitted when a plugin process has crashed. - */ - on(event: 'plugin-crashed', listener: (event: Event, - name: string, - version: string) => void): this; - once(event: 'plugin-crashed', listener: (event: Event, - name: string, - version: string) => void): this; - addListener(event: 'plugin-crashed', listener: (event: Event, - name: string, - version: string) => void): this; - removeListener(event: 'plugin-crashed', listener: (event: Event, - name: string, - version: string) => void): this; - /** - * Emitted when the preload script preloadPath throws an unhandled exception error. - */ - on(event: 'preload-error', listener: (event: Event, - preloadPath: string, - error: Error) => void): this; - once(event: 'preload-error', listener: (event: Event, - preloadPath: string, - error: Error) => void): this; - addListener(event: 'preload-error', listener: (event: Event, - preloadPath: string, - error: Error) => void): this; - removeListener(event: 'preload-error', listener: (event: Event, - preloadPath: string, - error: Error) => void): this; - /** - * Emitted when remote.getBuiltin() is called in the renderer process. Calling - * event.preventDefault() will prevent the module from being returned. Custom value - * can be returned by setting event.returnValue. - */ - on(event: 'remote-get-builtin', listener: (event: Event, - moduleName: string) => void): this; - once(event: 'remote-get-builtin', listener: (event: Event, - moduleName: string) => void): this; - addListener(event: 'remote-get-builtin', listener: (event: Event, - moduleName: string) => void): this; - removeListener(event: 'remote-get-builtin', listener: (event: Event, - moduleName: string) => void): this; - /** - * Emitted when remote.getCurrentWebContents() is called in the renderer process. - * Calling event.preventDefault() will prevent the object from being returned. - * Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; - once(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; - addListener(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; - removeListener(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; - /** - * Emitted when remote.getCurrentWindow() is called in the renderer process. - * Calling event.preventDefault() will prevent the object from being returned. - * Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-current-window', listener: (event: Event) => void): this; - once(event: 'remote-get-current-window', listener: (event: Event) => void): this; - addListener(event: 'remote-get-current-window', listener: (event: Event) => void): this; - removeListener(event: 'remote-get-current-window', listener: (event: Event) => void): this; - /** - * Emitted when remote.getGlobal() is called in the renderer process. Calling - * event.preventDefault() will prevent the global from being returned. Custom value - * can be returned by setting event.returnValue. - */ - on(event: 'remote-get-global', listener: (event: Event, - globalName: string) => void): this; - once(event: 'remote-get-global', listener: (event: Event, - globalName: string) => void): this; - addListener(event: 'remote-get-global', listener: (event: Event, - globalName: string) => void): this; - removeListener(event: 'remote-get-global', listener: (event: Event, - globalName: string) => void): this; - /** - * Emitted when .getWebContents() is called in the renderer process. - * Calling event.preventDefault() will prevent the object from being returned. - * Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-guest-web-contents', listener: (event: Event, - guestWebContents: WebContents) => void): this; - once(event: 'remote-get-guest-web-contents', listener: (event: Event, - guestWebContents: WebContents) => void): this; - addListener(event: 'remote-get-guest-web-contents', listener: (event: Event, - guestWebContents: WebContents) => void): this; - removeListener(event: 'remote-get-guest-web-contents', listener: (event: Event, - guestWebContents: WebContents) => void): this; - /** - * Emitted when remote.require() is called in the renderer process. Calling - * event.preventDefault() will prevent the module from being returned. Custom value - * can be returned by setting event.returnValue. - */ - on(event: 'remote-require', listener: (event: Event, - moduleName: string) => void): this; - once(event: 'remote-require', listener: (event: Event, - moduleName: string) => void): this; - addListener(event: 'remote-require', listener: (event: Event, - moduleName: string) => void): this; - removeListener(event: 'remote-require', listener: (event: Event, - moduleName: string) => void): this; - /** - * Emitted when the unresponsive web page becomes responsive again. - */ - on(event: 'responsive', listener: Function): this; - once(event: 'responsive', listener: Function): this; - addListener(event: 'responsive', listener: Function): this; - removeListener(event: 'responsive', listener: Function): this; - /** - * Emitted when bluetooth device needs to be selected on call to - * navigator.bluetooth.requestDevice. To use navigator.bluetooth api webBluetooth - * should be enabled. If event.preventDefault is not called, first available device - * will be selected. callback should be called with deviceId to be selected, - * passing empty string to callback will cancel the request. - */ - on(event: 'select-bluetooth-device', listener: (event: Event, - devices: BluetoothDevice[], - callback: (deviceId: string) => void) => void): this; - once(event: 'select-bluetooth-device', listener: (event: Event, - devices: BluetoothDevice[], - callback: (deviceId: string) => void) => void): this; - addListener(event: 'select-bluetooth-device', listener: (event: Event, - devices: BluetoothDevice[], - callback: (deviceId: string) => void) => void): this; - removeListener(event: 'select-bluetooth-device', listener: (event: Event, - devices: BluetoothDevice[], - callback: (deviceId: string) => void) => void): this; - /** - * Emitted when a client certificate is requested. The usage is the same with the - * select-client-certificate event of app. - */ - on(event: 'select-client-certificate', listener: (event: Event, - url: string, - certificateList: Certificate[], - callback: (certificate: Certificate) => void) => void): this; - once(event: 'select-client-certificate', listener: (event: Event, - url: string, - certificateList: Certificate[], - callback: (certificate: Certificate) => void) => void): this; - addListener(event: 'select-client-certificate', listener: (event: Event, - url: string, - certificateList: Certificate[], - callback: (certificate: Certificate) => void) => void): this; - removeListener(event: 'select-client-certificate', listener: (event: Event, - url: string, - certificateList: Certificate[], - callback: (certificate: Certificate) => void) => void): this; - /** - * Emitted when the web page becomes unresponsive. - */ - on(event: 'unresponsive', listener: Function): this; - once(event: 'unresponsive', listener: Function): this; - addListener(event: 'unresponsive', listener: Function): this; - removeListener(event: 'unresponsive', listener: Function): this; - /** - * Emitted when mouse moves over a link or the keyboard moves the focus to a link. - */ - on(event: 'update-target-url', listener: (event: Event, - url: string) => void): this; - once(event: 'update-target-url', listener: (event: Event, - url: string) => void): this; - addListener(event: 'update-target-url', listener: (event: Event, - url: string) => void): this; - removeListener(event: 'update-target-url', listener: (event: Event, - url: string) => void): this; - /** - * Emitted when a 's web contents is being attached to this web contents. - * Calling event.preventDefault() will destroy the guest page. This event can be - * used to configure webPreferences for the webContents of a before it's - * loaded, and provides the ability to set settings that can't be set via - * attributes. Note: The specified preload script option will be appear as - * preloadURL (not preload) in the webPreferences object emitted with this event. - */ - on(event: 'will-attach-webview', listener: (event: Event, - /** - * The web preferences that will be used by the guest page. This object can be - * modified to adjust the preferences for the guest page. - */ - webPreferences: any, - /** - * The other ` - */ - params: any) => void): this; - once(event: 'will-attach-webview', listener: (event: Event, - /** - * The web preferences that will be used by the guest page. This object can be - * modified to adjust the preferences for the guest page. - */ - webPreferences: any, - /** - * The other ` - */ - params: any) => void): this; - addListener(event: 'will-attach-webview', listener: (event: Event, - /** - * The web preferences that will be used by the guest page. This object can be - * modified to adjust the preferences for the guest page. - */ - webPreferences: any, - /** - * The other ` - */ - params: any) => void): this; - removeListener(event: 'will-attach-webview', listener: (event: Event, - /** - * The web preferences that will be used by the guest page. This object can be - * modified to adjust the preferences for the guest page. - */ - webPreferences: any, - /** - * The other ` - */ - params: any) => void): this; - /** - * Emitted when a user or the page wants to start navigation. It can happen when - * the window.location object is changed or a user clicks a link in the page. This - * event will not emit when the navigation is started programmatically with APIs - * like webContents.loadURL and webContents.back. It is also not emitted for - * in-page navigations, such as clicking anchor links or updating the - * window.location.hash. Use did-navigate-in-page event for this purpose. Calling - * event.preventDefault() will prevent the navigation. - */ - on(event: 'will-navigate', listener: (event: Event, - url: string) => void): this; - once(event: 'will-navigate', listener: (event: Event, - url: string) => void): this; - addListener(event: 'will-navigate', listener: (event: Event, - url: string) => void): this; - removeListener(event: 'will-navigate', listener: (event: Event, - url: string) => void): this; - /** - * Emitted when a beforeunload event handler is attempting to cancel a page unload. - * Calling event.preventDefault() will ignore the beforeunload event handler and - * allow the page to be unloaded. - */ - on(event: 'will-prevent-unload', listener: (event: Event) => void): this; - once(event: 'will-prevent-unload', listener: (event: Event) => void): this; - addListener(event: 'will-prevent-unload', listener: (event: Event) => void): this; - removeListener(event: 'will-prevent-unload', listener: (event: Event) => void): this; - /** - * Emitted as a server side redirect occurs during navigation. For example a 302 - * redirect. This event will be emitted after did-start-navigation and always - * before the did-redirect-navigation event for the same navigation. Calling - * event.preventDefault() will prevent the navigation (not just the redirect). - */ - on(event: 'will-redirect', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'will-redirect', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'will-redirect', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'will-redirect', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Adds the specified path to DevTools workspace. Must be used after DevTools - * creation: - */ - addWorkSpace(path: string): void; - /** - * Begin subscribing for presentation events and captured frames, the callback will - * be called with callback(image, dirtyRect) when there is a presentation event. - * The image is an instance of NativeImage that stores the captured frame. The - * dirtyRect is an object with x, y, width, height properties that describes which - * part of the page was repainted. If onlyDirty is set to true, image will only - * contain the repainted area. onlyDirty defaults to false. - */ - beginFrameSubscription(callback: (image: NativeImage, dirtyRect: Rectangle) => void): void; - /** - * Begin subscribing for presentation events and captured frames, the callback will - * be called with callback(image, dirtyRect) when there is a presentation event. - * The image is an instance of NativeImage that stores the captured frame. The - * dirtyRect is an object with x, y, width, height properties that describes which - * part of the page was repainted. If onlyDirty is set to true, image will only - * contain the repainted area. onlyDirty defaults to false. - */ - beginFrameSubscription(onlyDirty: boolean, callback: (image: NativeImage, dirtyRect: Rectangle) => void): void; - canGoBack(): boolean; - canGoForward(): boolean; - canGoToOffset(offset: number): boolean; - /** - * Captures a snapshot of the page within rect. Omitting rect will capture the - * whole visible page. - */ - capturePage(rect?: Rectangle): Promise; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(callback: (image: NativeImage) => void): void; - /** - * Clears the navigation history. - */ - clearHistory(): void; - /** - * Closes the devtools. - */ - closeDevTools(): void; - /** - * Executes the editing command copy in web page. - */ - copy(): void; - /** - * Copy the image at the given position to the clipboard. - */ - copyImageAt(x: number, y: number): void; - /** - * Executes the editing command cut in web page. - */ - cut(): void; - /** - * Executes the editing command delete in web page. - */ - delete(): void; - /** - * Disable device emulation enabled by webContents.enableDeviceEmulation. - */ - disableDeviceEmulation(): void; - /** - * Initiates a download of the resource at url without navigating. The - * will-download event of session will be triggered. - */ - downloadURL(url: string): void; - /** - * Enable device emulation with the given parameters. - */ - enableDeviceEmulation(parameters: Parameters): void; - /** - * End subscribing for frame presentation events. - */ - endFrameSubscription(): void; - /** - * Evaluates code in page. In the browser window some HTML APIs like - * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. Deprecated Soon - */ - executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; - /** - * Evaluates code in page. In the browser window some HTML APIs like - * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. - */ - executeJavaScript(code: string, userGesture?: boolean): Promise; - /** - * Starts a request to find all matches for the text in the web page. The result of - * the request can be obtained by subscribing to found-in-page event. - */ - findInPage(text: string, options?: FindInPageOptions): number; - /** - * Focuses the web page. - */ - focus(): void; - getFrameRate(): number; - getOSProcessId(): number; - /** - * Get the system printer list. - */ - getPrinters(): PrinterInfo[]; - getProcessId(): number; - getTitle(): string; - getType(): ('backgroundPage' | 'window' | 'browserView' | 'remote' | 'webview' | 'offscreen'); - getURL(): string; - getUserAgent(): string; - getWebRTCIPHandlingPolicy(): string; - getZoomFactor(): number; - getZoomLevel(): number; - /** - * Makes the browser go back a web page. - */ - goBack(): void; - /** - * Makes the browser go forward a web page. - */ - goForward(): void; - /** - * Navigates browser to the specified absolute web page index. - */ - goToIndex(index: number): void; - /** - * Navigates to the specified offset from the "current entry". - */ - goToOffset(offset: number): void; - /** - * Injects CSS into the current web page. - */ - insertCSS(css: string): void; - /** - * Inserts text to the focused element. - */ - insertText(text: string): void; - /** - * Starts inspecting element at position (x, y). - */ - inspectElement(x: number, y: number): void; - /** - * Opens the developer tools for the service worker context. - */ - inspectServiceWorker(): void; - /** - * Opens the developer tools for the shared worker context. - */ - inspectSharedWorker(): void; - /** - * Schedules a full repaint of the window this web contents is in. If offscreen - * rendering is enabled invalidates the frame and generates a new one through the - * 'paint' event. - */ - invalidate(): void; - isAudioMuted(): boolean; - isCrashed(): boolean; - isCurrentlyAudible(): boolean; - isDestroyed(): boolean; - isDevToolsFocused(): boolean; - isDevToolsOpened(): boolean; - isFocused(): boolean; - isLoading(): boolean; - isLoadingMainFrame(): boolean; - isOffscreen(): boolean; - isPainting(): boolean; - isWaitingForResponse(): boolean; - /** - * Loads the given file in the window, filePath should be a path to an HTML file - * relative to the root of your application. For instance an app structure like - * this: Would require code like this - */ - loadFile(filePath: string, options?: LoadFileOptions): Promise; - /** - * Loads the url in the window. The url must contain the protocol prefix, e.g. the - * http:// or file://. If the load should bypass http cache then use the pragma - * header to achieve it. - */ - loadURL(url: string, options?: LoadURLOptions): Promise; - /** - * Opens the devtools. When contents is a tag, the mode would be detach - * by default, explicitly passing an empty mode can force using last used dock - * state. - */ - openDevTools(options?: OpenDevToolsOptions): void; - /** - * Executes the editing command paste in web page. - */ - paste(): void; - /** - * Executes the editing command pasteAndMatchStyle in web page. - */ - pasteAndMatchStyle(): void; - /** - * Prints window's web page. When silent is set to true, Electron will pick the - * system's default printer if deviceName is empty and the default settings for - * printing. Calling window.print() in web page is equivalent to calling - * webContents.print({ silent: false, printBackground: false, deviceName: '' }). - * Use page-break-before: always; CSS style to force to print to a new page. - */ - print(options?: PrintOptions, callback?: (success: boolean) => void): void; - /** - * Prints window's web page as PDF with Chromium's preview printing custom - * settings. The landscape will be ignored if @page CSS at-rule is used in the web - * page. By default, an empty options will be regarded as: Use page-break-before: - * always; CSS style to force to print to a new page. An example of - * webContents.printToPDF: - */ - printToPDF(options: PrintToPDFOptions): Promise; - /** - * Prints window's web page as PDF with Chromium's preview printing custom - * settings. The callback will be called with callback(error, data) on completion. - * The data is a Buffer that contains the generated PDF data. Deprecated Soon - */ - printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; - /** - * Executes the editing command redo in web page. - */ - redo(): void; - /** - * Reloads the current web page. - */ - reload(): void; - /** - * Reloads current page and ignores cache. - */ - reloadIgnoringCache(): void; - /** - * Removes the specified path from DevTools workspace. - */ - removeWorkSpace(path: string): void; - /** - * Executes the editing command replace in web page. - */ - replace(text: string): void; - /** - * Executes the editing command replaceMisspelling in web page. - */ - replaceMisspelling(text: string): void; - savePage(fullPath: string, saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML'): Promise; - /** - * Executes the editing command selectAll in web page. - */ - selectAll(): void; - /** - * Send an asynchronous message to renderer process via channel, you can also send - * arbitrary arguments. Arguments will be serialized in JSON internally and hence - * no functions or prototype chain will be included. The renderer process can - * handle the message by listening to channel with the ipcRenderer module. An - * example of sending messages from the main process to the renderer process: - */ - send(channel: string, ...args: any[]): void; - /** - * Sends an input event to the page. Note: The BrowserWindow containing the - * contents needs to be focused for sendInputEvent() to work. For keyboard events, - * the event object also have following properties: For mouse events, the event - * object also have following properties: For the mouseWheel event, the event - * object also have following properties: - */ - sendInputEvent(event: Event): void; - /** - * Send an asynchronous message to a specific frame in a renderer process via - * channel. Arguments will be serialized as JSON internally and as such no - * functions or prototype chains will be included. The renderer process can handle - * the message by listening to channel with the ipcRenderer module. If you want to - * get the frameId of a given renderer context you should use the - * webFrame.routingId value. E.g. You can also read frameId from all incoming IPC - * messages in the main process. - */ - sendToFrame(frameId: number, channel: string, ...args: any[]): void; - /** - * Mute the audio on the current web page. - */ - setAudioMuted(muted: boolean): void; - /** - * Controls whether or not this WebContents will throttle animations and timers - * when the page becomes backgrounded. This also affects the Page Visibility API. - */ - setBackgroundThrottling(allowed: boolean): void; - /** - * Uses the devToolsWebContents as the target WebContents to show devtools. The - * devToolsWebContents must not have done any navigation, and it should not be used - * for other purposes after the call. By default Electron manages the devtools by - * creating an internal WebContents with native view, which developers have very - * limited control of. With the setDevToolsWebContents method, developers can use - * any WebContents to show the devtools in it, including BrowserWindow, BrowserView - * and tag. Note that closing the devtools does not destroy the - * devToolsWebContents, it is caller's responsibility to destroy - * devToolsWebContents. An example of showing devtools in a tag: An - * example of showing devtools in a BrowserWindow: - */ - setDevToolsWebContents(devToolsWebContents: WebContents): void; - /** - * If offscreen rendering is enabled sets the frame rate to the specified number. - * Only values between 1 and 60 are accepted. - */ - setFrameRate(fps: number): void; - /** - * Ignore application menu shortcuts while this web contents is focused. - */ - setIgnoreMenuShortcuts(ignore: boolean): void; - /** - * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. - */ - setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Overrides the user agent for this web page. - */ - setUserAgent(userAgent: string): void; - /** - * Sets the maximum and minimum pinch-to-zoom level. - */ - setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Setting the WebRTC IP handling policy allows you to control which IPs are - * exposed via WebRTC. See BrowserLeaks for more details. - */ - setWebRTCIPHandlingPolicy(policy: 'default' | 'default_public_interface_only' | 'default_public_and_private_interfaces' | 'disable_non_proxied_udp'): void; - /** - * Changes the zoom factor to the specified factor. Zoom factor is zoom percent - * divided by 100, so 300% = 3.0. - */ - setZoomFactor(factor: number): void; - /** - * 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. The formula for this is - * scale := 1.2 ^ level. - */ - setZoomLevel(level: number): void; - /** - * Shows pop-up dictionary that searches the selected word on the page. - */ - showDefinitionForSelection(): void; - /** - * Sets the item as dragging item for current drag-drop operation, file is the - * absolute path of the file to be dragged, and icon is the image showing under the - * cursor when dragging. - */ - startDrag(item: Item): void; - /** - * If offscreen rendering is enabled and not painting, start painting. - */ - startPainting(): void; - /** - * Stops any pending navigation. - */ - stop(): void; - /** - * Stops any findInPage request for the webContents with the provided action. - */ - stopFindInPage(action: 'clearSelection' | 'keepSelection' | 'activateSelection'): void; - /** - * If offscreen rendering is enabled and painting, stop painting. - */ - stopPainting(): void; - /** - * Takes a V8 heap snapshot and saves it to filePath. - */ - takeHeapSnapshot(filePath: string): Promise; - /** - * Toggles the developer tools. - */ - toggleDevTools(): void; - /** - * Executes the editing command undo in web page. - */ - undo(): void; - /** - * Executes the editing command unselect in web page. - */ - unselect(): void; - debugger: Debugger; - devToolsWebContents: WebContents; - hostWebContents: WebContents; - id: number; - session: Session; - } - - interface WebFrame extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/web-frame - - /** - * Attempts to free memory that is no longer being used (like images from a - * previous navigation). Note that blindly calling this method probably makes - * Electron slower since it will have to refill these emptied caches, you should - * only call it if an event in your app has occurred that makes you think your page - * is actually using less memory (i.e. you have navigated from a super heavy page - * to a mostly empty one, and intend to stay there). - */ - clearCache(): void; - /** - * Evaluates code in page. In the browser window some HTML APIs like - * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. - */ - executeJavaScript(code: string, userGesture?: boolean): Promise; - /** - * Evaluates code in page. In the browser window some HTML APIs like - * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. Deprecated Soon - */ - executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; - /** - * Works like executeJavaScript but evaluates scripts in an isolated context. - * Deprecated Soon - */ - executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): Promise; - /** - * Works like executeJavaScript but evaluates scripts in an isolated context. - */ - executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean): Promise; - findFrameByName(name: string): WebFrame; - findFrameByRoutingId(routingId: number): WebFrame; - getFrameForSelector(selector: string): WebFrame; - /** - * Returns an object describing usage information of Blink's internal memory - * caches. This will generate: - */ - getResourceUsage(): ResourceUsage; - getZoomFactor(): number; - getZoomLevel(): number; - /** - * Inserts css as a style sheet in the document. - */ - insertCSS(css: string): void; - /** - * Inserts text to the focused element. - */ - insertText(text: string): void; - /** - * Set the content security policy of the isolated world. - */ - setIsolatedWorldContentSecurityPolicy(worldId: number, csp: string): void; - /** - * Set the name of the isolated world. Useful in devtools. - */ - setIsolatedWorldHumanReadableName(worldId: number, name: string): void; - /** - * Set the security origin, content security policy and name of the isolated world. - * Note: If the csp is specified, then the securityOrigin also has to be specified. - */ - setIsolatedWorldInfo(worldId: number, info: Info): void; - /** - * Set the security origin of the isolated world. - */ - setIsolatedWorldSecurityOrigin(worldId: number, securityOrigin: string): void; - /** - * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. - */ - setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Sets a provider for spell checking in input fields and text areas. The provider - * must be an object that has a spellCheck method that accepts an array of - * individual words for spellchecking. The spellCheck function runs asynchronously - * and calls the callback function with an array of misspelt words when complete. - * An example of using node-spellchecker as provider: - */ - setSpellCheckProvider(language: string, provider: Provider): void; - /** - * Sets the maximum and minimum pinch-to-zoom level. - */ - setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Changes the zoom factor to the specified factor. Zoom factor is zoom percent - * divided by 100, so 300% = 3.0. - */ - setZoomFactor(factor: number): void; - /** - * 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; - /** - * A WebFrame representing the first child frame of webFrame, the property would be - * null if webFrame has no children or if first child is not in the current - * renderer process. - */ - firstChild?: WebFrame; - /** - * A WebFrame representing next sibling frame, the property would be null if - * webFrame is the last frame in its parent or if the next sibling is not in the - * current renderer process. - */ - nextSibling?: WebFrame; - /** - * A WebFrame representing the frame which opened webFrame, the property would be - * null if there's no opener or opener is not in the current renderer process. - */ - opener?: WebFrame; - /** - * A WebFrame representing parent frame of webFrame, the property would be null if - * webFrame is top or parent is not in the current renderer process. - */ - parent?: WebFrame; - /** - * An Integer representing the unique frame id in the current renderer process. - * Distinct WebFrame instances that refer to the same underlying frame will have - * the same routingId. - */ - routingId?: number; - /** - * A WebFrame representing top frame in frame hierarchy to which webFrame belongs, - * the property would be null if top frame is not in the current renderer process. - */ - top?: WebFrame; - } - - class WebRequest extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/web-request - - /** - * The listener will be called with listener(details) when a server initiated - * redirect is about to occur. - */ - onBeforeRedirect(listener: ((details: OnBeforeRedirectDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when a server initiated - * redirect is about to occur. - */ - onBeforeRedirect(filter: OnBeforeRedirectFilter, listener: ((details: OnBeforeRedirectDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) when a request is - * about to occur. The uploadData is an array of UploadData objects. The callback - * has to be called with an response object. Some examples of valid urls: - */ - onBeforeRequest(listener: ((details: OnBeforeRequestDetails, callback: (response: Response) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) when a request is - * about to occur. The uploadData is an array of UploadData objects. The callback - * has to be called with an response object. Some examples of valid urls: - */ - onBeforeRequest(filter: OnBeforeRequestFilter, listener: ((details: OnBeforeRequestDetails, callback: (response: Response) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) before sending an - * HTTP request, once the request headers are available. This may occur after a TCP - * connection is made to the server, but before any http data is sent. The callback - * has to be called with an response object. - */ - onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: ((details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) before sending an - * HTTP request, once the request headers are available. This may occur after a TCP - * connection is made to the server, but before any http data is sent. The callback - * has to be called with an response object. - */ - onBeforeSendHeaders(listener: ((details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details) when a request is completed. - */ - onCompleted(filter: OnCompletedFilter, listener: ((details: OnCompletedDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when a request is completed. - */ - onCompleted(listener: ((details: OnCompletedDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when an error occurs. - */ - onErrorOccurred(listener: ((details: OnErrorOccurredDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when an error occurs. - */ - onErrorOccurred(filter: OnErrorOccurredFilter, listener: ((details: OnErrorOccurredDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) when HTTP response - * headers of a request have been received. The callback has to be called with an - * response object. - */ - onHeadersReceived(filter: OnHeadersReceivedFilter, listener: ((details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) when HTTP response - * headers of a request have been received. The callback has to be called with an - * response object. - */ - onHeadersReceived(listener: ((details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details) when first byte of the - * response body is received. For HTTP requests, this means that the status line - * and response headers are available. - */ - onResponseStarted(listener: ((details: OnResponseStartedDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when first byte of the - * response body is received. For HTTP requests, this means that the status line - * and response headers are available. - */ - onResponseStarted(filter: OnResponseStartedFilter, listener: ((details: OnResponseStartedDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) just before a request is - * going to be sent to the server, modifications of previous onBeforeSendHeaders - * response are visible by the time this listener is fired. - */ - onSendHeaders(filter: OnSendHeadersFilter, listener: ((details: OnSendHeadersDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) just before a request is - * going to be sent to the server, modifications of previous onBeforeSendHeaders - * response are visible by the time this listener is fired. - */ - onSendHeaders(listener: ((details: OnSendHeadersDetails) => void) | (null)): void; - } - - interface WebSource { - - // Docs: http://electronjs.org/docs/api/structures/web-source - - code: string; - /** - * Default is 1. - */ - startLine?: number; - url?: string; - } - - interface WebviewTag extends HTMLElement { - - // Docs: http://electronjs.org/docs/api/webview-tag - - /** - * Fired when a load has committed. This includes navigation within the current - * document as well as subframe document-level loads, but does not include - * asynchronous resource loads. - */ - addEventListener(event: 'load-commit', listener: (event: LoadCommitEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'load-commit', listener: (event: LoadCommitEvent) => void): this; - /** - * Fired when the navigation is done, i.e. the spinner of the tab will stop - * spinning, and the onload event is dispatched. - */ - addEventListener(event: 'did-finish-load', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-finish-load', listener: (event: Event) => void): this; - /** - * This event is like did-finish-load, but fired when the load failed or was - * cancelled, e.g. window.stop() is invoked. - */ - addEventListener(event: 'did-fail-load', listener: (event: DidFailLoadEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-fail-load', listener: (event: DidFailLoadEvent) => void): this; - /** - * Fired when a frame has done navigation. - */ - addEventListener(event: 'did-frame-finish-load', listener: (event: DidFrameFinishLoadEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-frame-finish-load', listener: (event: DidFrameFinishLoadEvent) => void): this; - /** - * Corresponds to the points in time when the spinner of the tab starts spinning. - */ - addEventListener(event: 'did-start-loading', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-start-loading', listener: (event: Event) => void): this; - /** - * Corresponds to the points in time when the spinner of the tab stops spinning. - */ - addEventListener(event: 'did-stop-loading', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-stop-loading', listener: (event: Event) => void): this; - /** - * Fired when document in the given frame is loaded. - */ - addEventListener(event: 'dom-ready', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'dom-ready', listener: (event: Event) => void): this; - /** - * Fired when page title is set during navigation. explicitSet is false when title - * is synthesized from file url. - */ - addEventListener(event: 'page-title-updated', listener: (event: PageTitleUpdatedEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'page-title-updated', listener: (event: PageTitleUpdatedEvent) => void): this; - /** - * Fired when page receives favicon urls. - */ - addEventListener(event: 'page-favicon-updated', listener: (event: PageFaviconUpdatedEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'page-favicon-updated', listener: (event: PageFaviconUpdatedEvent) => void): this; - /** - * Fired when page enters fullscreen triggered by HTML API. - */ - addEventListener(event: 'enter-html-full-screen', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'enter-html-full-screen', listener: (event: Event) => void): this; - /** - * Fired when page leaves fullscreen triggered by HTML API. - */ - addEventListener(event: 'leave-html-full-screen', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'leave-html-full-screen', listener: (event: Event) => void): this; - /** - * Fired when the guest window logs a console message. The following example code - * forwards all log messages to the embedder's console without regard for log level - * or other properties. - */ - addEventListener(event: 'console-message', listener: (event: ConsoleMessageEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'console-message', listener: (event: ConsoleMessageEvent) => void): this; - /** - * Fired when a result is available for webview.findInPage request. - */ - addEventListener(event: 'found-in-page', listener: (event: FoundInPageEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'found-in-page', listener: (event: FoundInPageEvent) => void): this; - /** - * Fired when the guest page attempts to open a new browser window. The following - * example code opens the new url in system's default browser. - */ - addEventListener(event: 'new-window', listener: (event: NewWindowEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'new-window', listener: (event: NewWindowEvent) => void): this; - /** - * Emitted when a user or the page wants to start navigation. It can happen when - * the window.location object is changed or a user clicks a link in the page. This - * event will not emit when the navigation is started programmatically with APIs - * like .loadURL and .back. It is also not emitted during in-page - * navigation, such as clicking anchor links or updating the window.location.hash. - * Use did-navigate-in-page event for this purpose. Calling event.preventDefault() - * does NOT have any effect. - */ - addEventListener(event: 'will-navigate', listener: (event: WillNavigateEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'will-navigate', listener: (event: WillNavigateEvent) => void): this; - /** - * Emitted when a navigation is done. This event is not emitted for in-page - * navigations, such as clicking anchor links or updating the window.location.hash. - * Use did-navigate-in-page event for this purpose. - */ - addEventListener(event: 'did-navigate', listener: (event: DidNavigateEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-navigate', listener: (event: DidNavigateEvent) => void): this; - /** - * Emitted when an in-page navigation happened. When in-page navigation happens, - * the page URL changes but does not cause navigation outside of the page. Examples - * of this occurring are when anchor links are clicked or when the DOM hashchange - * event is triggered. - */ - addEventListener(event: 'did-navigate-in-page', listener: (event: DidNavigateInPageEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-navigate-in-page', listener: (event: DidNavigateInPageEvent) => void): this; - /** - * Fired when the guest page attempts to close itself. The following example code - * navigates the webview to about:blank when the guest attempts to close itself. - */ - addEventListener(event: 'close', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'close', listener: (event: Event) => void): this; - /** - * Fired when the guest page has sent an asynchronous message to embedder page. - * With sendToHost method and ipc-message event you can communicate between guest - * page and embedder page: - */ - addEventListener(event: 'ipc-message', listener: (event: IpcMessageEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'ipc-message', listener: (event: IpcMessageEvent) => void): this; - /** - * Fired when the renderer process is crashed. - */ - addEventListener(event: 'crashed', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'crashed', listener: (event: Event) => void): this; - /** - * Fired when a plugin process is crashed. - */ - addEventListener(event: 'plugin-crashed', listener: (event: PluginCrashedEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'plugin-crashed', listener: (event: PluginCrashedEvent) => void): this; - /** - * Fired when the WebContents is destroyed. - */ - addEventListener(event: 'destroyed', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'destroyed', listener: (event: Event) => void): this; - /** - * Emitted when media starts playing. - */ - addEventListener(event: 'media-started-playing', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'media-started-playing', listener: (event: Event) => void): this; - /** - * Emitted when media is paused or done playing. - */ - addEventListener(event: 'media-paused', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'media-paused', listener: (event: Event) => void): this; - /** - * Emitted when a page's theme color changes. This is usually due to encountering a - * meta tag: - */ - addEventListener(event: 'did-change-theme-color', listener: (event: DidChangeThemeColorEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-change-theme-color', listener: (event: DidChangeThemeColorEvent) => void): this; - /** - * Emitted when mouse moves over a link or the keyboard moves the focus to a link. - */ - addEventListener(event: 'update-target-url', listener: (event: UpdateTargetUrlEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'update-target-url', listener: (event: UpdateTargetUrlEvent) => void): this; - /** - * Emitted when DevTools is opened. - */ - addEventListener(event: 'devtools-opened', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'devtools-opened', listener: (event: Event) => void): this; - /** - * Emitted when DevTools is closed. - */ - addEventListener(event: 'devtools-closed', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'devtools-closed', listener: (event: Event) => void): this; - /** - * Emitted when DevTools is focused / opened. - */ - addEventListener(event: 'devtools-focused', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'devtools-focused', listener: (event: Event) => void): this; - addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): void; - addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; - removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): void; - removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; - canGoBack(): boolean; - canGoForward(): boolean; - canGoToOffset(offset: number): boolean; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(callback: (image: NativeImage) => void): void; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; - /** - * Captures a snapshot of the page within rect. Omitting rect will capture the - * whole visible page. - */ - capturePage(rect?: Rectangle): Promise; - /** - * Clears the navigation history. - */ - clearHistory(): void; - /** - * Closes the DevTools window of guest page. - */ - closeDevTools(): void; - /** - * Executes editing command copy in page. - */ - copy(): void; - /** - * Executes editing command cut in page. - */ - cut(): void; - /** - * Executes editing command delete in page. - */ - delete(): void; - /** - * Initiates a download of the resource at url without navigating. - */ - downloadURL(url: string): void; - /** - * Evaluates code in page. If userGesture is set, it will create the user gesture - * context in the page. HTML APIs like requestFullScreen, which require user - * action, can take advantage of this option for automation. Deprecated Soon - */ - executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; - /** - * Evaluates code in page. If userGesture is set, it will create the user gesture - * context in the page. HTML APIs like requestFullScreen, which require user - * action, can take advantage of this option for automation. - */ - executeJavaScript(code: string, userGesture?: boolean): Promise; - /** - * Starts a request to find all matches for the text in the web page. The result of - * the request can be obtained by subscribing to found-in-page event. - */ - findInPage(text: string, options?: FindInPageOptions): number; - getTitle(): string; - getURL(): string; - getUserAgent(): string; - /** - * It depends on the remote module, it is therefore not available when this module - * is disabled. - */ - getWebContents(): WebContents; - getWebContentsId(): number; - getZoomFactor(): number; - getZoomLevel(): number; - /** - * Makes the guest page go back. - */ - goBack(): void; - /** - * Makes the guest page go forward. - */ - goForward(): void; - /** - * Navigates to the specified absolute index. - */ - goToIndex(index: number): void; - /** - * Navigates to the specified offset from the "current entry". - */ - goToOffset(offset: number): void; - /** - * Injects CSS into the guest page. - */ - insertCSS(css: string): void; - /** - * Inserts text to the focused element. - */ - insertText(text: string): void; - /** - * Starts inspecting element at position (x, y) of guest page. - */ - inspectElement(x: number, y: number): void; - /** - * Opens the DevTools for the service worker context present in the guest page. - */ - inspectServiceWorker(): void; - /** - * Opens the DevTools for the shared worker context present in the guest page. - */ - inspectSharedWorker(): void; - isAudioMuted(): boolean; - isCrashed(): boolean; - isCurrentlyAudible(): boolean; - isDevToolsFocused(): boolean; - isDevToolsOpened(): boolean; - isLoading(): boolean; - isLoadingMainFrame(): boolean; - isWaitingForResponse(): boolean; - /** - * Loads the url in the webview, the url must contain the protocol prefix, e.g. the - * http:// or file://. - */ - loadURL(url: string, options?: LoadURLOptions): Promise; - /** - * Opens a DevTools window for guest page. - */ - openDevTools(): void; - /** - * Executes editing command paste in page. - */ - paste(): void; - /** - * Executes editing command pasteAndMatchStyle in page. - */ - pasteAndMatchStyle(): void; - /** - * Prints webview's web page. Same as webContents.print([options]). - */ - print(options?: PrintOptions): void; - /** - * Prints webview's web page as PDF, Same as webContents.printToPDF(options, - * callback). Deprecated Soon - */ - printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; - /** - * Prints webview's web page as PDF, Same as webContents.printToPDF(options). - */ - printToPDF(options: PrintToPDFOptions): Promise; - /** - * Executes editing command redo in page. - */ - redo(): void; - /** - * Reloads the guest page. - */ - reload(): void; - /** - * Reloads the guest page and ignores cache. - */ - reloadIgnoringCache(): void; - /** - * Executes editing command replace in page. - */ - replace(text: string): void; - /** - * Executes editing command replaceMisspelling in page. - */ - replaceMisspelling(text: string): void; - /** - * Executes editing command selectAll in page. - */ - selectAll(): void; - /** - * Send an asynchronous message to renderer process via channel, you can also send - * arbitrary arguments. The renderer process can handle the message by listening to - * the channel event with the ipcRenderer module. See webContents.send for - * examples. - */ - send(channel: string, ...args: any[]): void; - /** - * Sends an input event to the page. See webContents.sendInputEvent for detailed - * description of event object. - */ - sendInputEvent(event: any): void; - /** - * Set guest page muted. - */ - setAudioMuted(muted: boolean): void; - /** - * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. - */ - setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Overrides the user agent for the guest page. - */ - setUserAgent(userAgent: string): void; - /** - * Sets the maximum and minimum pinch-to-zoom level. - */ - setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Changes the zoom factor to the specified factor. Zoom factor is zoom percent - * divided by 100, so 300% = 3.0. - */ - setZoomFactor(factor: number): void; - /** - * 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. The formula for this is - * scale := 1.2 ^ level. - */ - setZoomLevel(level: number): void; - /** - * Shows pop-up dictionary that searches the selected word on the page. - */ - showDefinitionForSelection(): void; - /** - * Stops any pending navigation. - */ - stop(): void; - /** - * Stops any findInPage request for the webview with the provided action. - */ - stopFindInPage(action: 'clearSelection' | 'keepSelection' | 'activateSelection'): void; - /** - * Executes editing command undo in page. - */ - undo(): void; - /** - * Executes editing command unselect in page. - */ - unselect(): void; - /** - * When this attribute is present the guest page will be allowed to open new - * windows. Popups are disabled by default. - */ - // allowpopups?: string; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * A list of strings which specifies the blink features to be disabled separated by - * ,. The full list of supported feature strings can be found in the - * RuntimeEnabledFeatures.json5 file. - */ - disableblinkfeatures?: string; - /** - * When this attribute is present the guest page will have web security disabled. - * Web security is enabled by default. - */ - // disablewebsecurity?: string; ### VSCODE CHANGE(https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * A list of strings which specifies the blink features to be enabled separated by - * ,. The full list of supported feature strings can be found in the - * RuntimeEnabledFeatures.json5 file. - */ - enableblinkfeatures?: string; - /** - * When this attribute is false the guest page in webview will not have access to - * the remote module. The remote module is available by default. - */ - enableremotemodule?: string; - /** - * Sets the referrer URL for the guest page. - */ - httpreferrer?: string; - /** - * When this attribute is present the guest page in webview will have node - * integration and can use node APIs like require and process to access low level - * system resources. Node integration is disabled by default in the guest page. - */ - nodeintegration?: string; - /** - * Experimental option for enabling NodeJS support in sub-frames such as iframes - * inside the webview. All your preloads will load for every iframe, you can use - * process.isMainFrame to determine if you are in the main frame or not. This - * option is disabled by default in the guest page. - */ - nodeintegrationinsubframes?: string; - /** - * Sets the session used by the page. If partition starts with persist:, the page - * will use a persistent session available to all pages in the app with the same - * partition. if there is no persist: prefix, the page will use an in-memory - * session. By assigning the same partition, multiple pages can share the same - * session. If the partition is unset then default session of the app will be used. - * This value can only be modified before the first navigation, since the session - * of an active renderer process cannot change. Subsequent attempts to modify the - * value will fail with a DOM exception. - */ - partition?: string; - /** - * When this attribute is present the guest page in webview will be able to use - * browser plugins. Plugins are disabled by default. - */ - plugins?: string; - /** - * Specifies a script that will be loaded before other scripts run in the guest - * page. The protocol of script's URL must be either file: or asar:, because it - * will be loaded by require in guest page under the hood. When the guest page - * doesn't have node integration this script will still have access to all Node - * APIs, but global objects injected by Node will be deleted after this script has - * finished executing. Note: This option will be appear as preloadURL (not preload) - * in the webPreferences specified to the will-attach-webview event. - */ - preload?: string; - /** - * Returns the visible URL. Writing to this attribute initiates top-level - * navigation. Assigning src its own value will reload the current page. The src - * attribute can also accept data URLs, such as data:text/plain,Hello, world!. - */ - src?: string; - /** - * Sets the user agent for the guest page before the page is navigated to. Once the - * page is loaded, use the setUserAgent method to change the user agent. - */ - useragent?: string; - /** - * A list of strings which specifies the web preferences to be set on the webview, - * separated by ,. The full list of supported preference strings can be found in - * BrowserWindow. The string follows the same format as the features string in - * window.open. A name by itself is given a true boolean value. A preference can be - * set to another value by including an =, followed by the value. Special values - * yes and 1 are interpreted as true, while no and 0 are interpreted as false. - */ - webpreferences?: string; - } - - interface AboutPanelOptionsOptions { - /** - * The app's name. - */ - applicationName?: string; - /** - * The app's version. - */ - applicationVersion?: string; - /** - * Copyright information. - */ - copyright?: string; - /** - * The app's build version number. - */ - version?: string; - /** - * Credit information. - */ - credits?: string; - /** - * The app's website. - */ - website?: string; - /** - * Path to the app's icon. Will be shown as 64x64 pixels while retaining aspect - * ratio. - */ - iconPath?: string; - } - - interface AddRepresentationOptions { - /** - * The scale factor to add the image representation for. - */ - scaleFactor: number; - /** - * Defaults to 0. Required if a bitmap buffer is specified as buffer. - */ - width?: number; - /** - * Defaults to 0. Required if a bitmap buffer is specified as buffer. - */ - height?: number; - /** - * The buffer containing the raw image data. - */ - buffer?: Buffer; - /** - * The data URL containing either a base 64 encoded PNG or JPEG image. - */ - dataURL?: string; - } - - interface AnimationSettings { - /** - * Returns true if rich animations should be rendered. Looks at session type (e.g. - * remote desktop) and accessibility settings to give guidance for heavy - * animations. - */ - shouldRenderRichAnimation: boolean; - /** - * Determines on a per-platform basis whether scroll animations (e.g. produced by - * home/end key) should be enabled. - */ - scrollAnimationsEnabledBySystem: boolean; - /** - * Determines whether the user desires reduced motion based on platform APIs. - */ - prefersReducedMotion: boolean; - } - - interface AppDetailsOptions { - /** - * Window's . It has to be set, otherwise the other options will have no effect. - */ - appId?: string; - /** - * Window's . - */ - appIconPath?: string; - /** - * Index of the icon in appIconPath. Ignored when appIconPath is not set. Default - * is 0. - */ - appIconIndex?: number; - /** - * Window's . - */ - relaunchCommand?: string; - /** - * Window's . - */ - relaunchDisplayName?: string; - } - - interface AuthInfo { - isProxy: boolean; - scheme: string; - host: string; - port: number; - realm: string; - } - - interface AutoResizeOptions { - /** - * If true, the view's width will grow and shrink together with the window. false - * by default. - */ - width: boolean; - /** - * If true, the view's height will grow and shrink together with the window. false - * by default. - */ - height: boolean; - /** - * If true, the view's x position and width will grow and shrink proportionly with - * the window. false by default. - */ - horizontal: boolean; - /** - * If true, the view's y position and height will grow and shrink proportinaly with - * the window. false by default. - */ - vertical: boolean; - } - - interface BitmapOptions { - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface BrowserViewConstructorOptions { - /** - * See . - */ - webPreferences?: WebPreferences; - } - - interface BrowserWindowConstructorOptions { - /** - * Window's width in pixels. Default is 800. - */ - width?: number; - /** - * Window's height in pixels. Default is 600. - */ - height?: number; - /** - * ( if y is used) Window's left offset from screen. Default is to center the - * window. - */ - x?: number; - /** - * ( if x is used) Window's top offset from screen. Default is to center the - * window. - */ - y?: number; - /** - * The width and height would be used as web page's size, which means the actual - * window's size will include window frame's size and be slightly larger. Default - * is false. - */ - useContentSize?: boolean; - /** - * Show window in the center of the screen. - */ - center?: boolean; - /** - * Window's minimum width. Default is 0. - */ - minWidth?: number; - /** - * Window's minimum height. Default is 0. - */ - minHeight?: number; - /** - * Window's maximum width. Default is no limit. - */ - maxWidth?: number; - /** - * Window's maximum height. Default is no limit. - */ - maxHeight?: number; - /** - * Whether window is resizable. Default is true. - */ - resizable?: boolean; - /** - * Whether window is movable. This is not implemented on Linux. Default is true. - */ - movable?: boolean; - /** - * Whether window is minimizable. This is not implemented on Linux. Default is - * true. - */ - minimizable?: boolean; - /** - * Whether window is maximizable. This is not implemented on Linux. Default is - * true. - */ - maximizable?: boolean; - /** - * Whether window is closable. This is not implemented on Linux. Default is true. - */ - closable?: boolean; - /** - * Whether the window can be focused. Default is true. On Windows setting - * focusable: false also implies setting skipTaskbar: true. On Linux setting - * focusable: false makes the window stop interacting with wm, so the window will - * always stay on top in all workspaces. - */ - focusable?: boolean; - /** - * Whether the window should always stay on top of other windows. Default is false. - */ - alwaysOnTop?: boolean; - /** - * Whether the window should show in fullscreen. When explicitly set to false the - * fullscreen button will be hidden or disabled on macOS. Default is false. - */ - fullscreen?: boolean; - /** - * Whether the window can be put into fullscreen mode. On macOS, also whether the - * maximize/zoom button should toggle full screen mode or maximize window. Default - * is true. - */ - fullscreenable?: boolean; - /** - * Use pre-Lion fullscreen on macOS. Default is false. - */ - simpleFullscreen?: boolean; - /** - * Whether to show the window in taskbar. Default is false. - */ - skipTaskbar?: boolean; - /** - * The kiosk mode. Default is false. - */ - kiosk?: boolean; - /** - * Default window title. Default is "Electron". If the HTML tag is defined - * in the HTML file loaded by loadURL(), this property will be - * ignored. - */ - title?: string; - /** - * The window icon. On Windows it is recommended to use ICO icons to get best - * visual effects, you can also leave it undefined so the executable's icon will be - * used. - */ - icon?: (NativeImage) | (string); - /** - * Whether window should be shown when created. Default is true. - */ - show?: boolean; - /** - * Specify false to create a . Default is true. - */ - frame?: boolean; - /** - * Specify parent window. Default is null. - */ - parent?: BrowserWindow; - /** - * Whether this is a modal window. This only works when the window is a child - * window. Default is false. - */ - modal?: boolean; - /** - * Whether the web view accepts a single mouse-down event that simultaneously - * activates the window. Default is false. - */ - acceptFirstMouse?: boolean; - /** - * Whether to hide cursor when typing. Default is false. - */ - disableAutoHideCursor?: boolean; - /** - * Auto hide the menu bar unless the Alt key is pressed. Default is false. - */ - autoHideMenuBar?: boolean; - /** - * Enable the window to be resized larger than screen. Default is false. - */ - enableLargerThanScreen?: boolean; - /** - * Window's background color as a hexadecimal value, like #66CD00 or #FFF or - * #80FFFFFF (alpha in #AARRGGBB format is supported if transparent is set to - * true). Default is #FFF (white). - */ - backgroundColor?: string; - /** - * Whether window should have a shadow. This is only implemented on macOS. Default - * is true. - */ - hasShadow?: boolean; - /** - * Set the initial opacity of the window, between 0.0 (fully transparent) and 1.0 - * (fully opaque). This is only implemented on Windows and macOS. - */ - opacity?: number; - /** - * Forces using dark theme for the window, only works on some GTK+3 desktop - * environments. Default is false. - */ - darkTheme?: boolean; - /** - * Makes the window . Default is false. - */ - transparent?: boolean; - /** - * The type of window, default is normal window. See more about this below. - */ - type?: string; - /** - * The style of window title bar. Default is default. Possible values are: - */ - titleBarStyle?: ('default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover'); - /** - * Shows the title in the title bar in full screen mode on macOS for all - * titleBarStyle options. Default is false. - */ - fullscreenWindowTitle?: boolean; - /** - * Use WS_THICKFRAME style for frameless windows on Windows, which adds standard - * window frame. Setting it to false will remove window shadow and window - * animations. Default is true. - */ - thickFrame?: boolean; - /** - * Add a type of vibrancy effect to the window, only on macOS. Can be - * appearance-based, light, dark, titlebar, selection, menu, popover, sidebar, - * medium-light or ultra-dark. Please note that using frame: false in combination - * with a vibrancy value requires that you use a non-default titleBarStyle as well. - */ - vibrancy?: ('appearance-based' | 'light' | 'dark' | 'titlebar' | 'selection' | 'menu' | 'popover' | 'sidebar' | 'medium-light' | 'ultra-dark'); - /** - * Controls the behavior on macOS when option-clicking the green stoplight button - * on the toolbar or by clicking the Window > Zoom menu item. If true, the window - * will grow to the preferred width of the web page when zoomed, false will cause - * it to zoom to the width of the screen. This will also affect the behavior when - * calling maximize() directly. Default is false. - */ - zoomToPageWidth?: boolean; - /** - * Tab group name, allows opening the window as a native tab on macOS 10.12+. - * Windows with the same tabbing identifier will be grouped together. This also - * adds a native new tab button to your window's tab bar and allows your app and - * window to receive the new-window-for-tab event. - */ - tabbingIdentifier?: string; - /** - * Settings of web page's features. - */ - webPreferences?: WebPreferences; - } - - interface CertificateTrustDialogOptions { - /** - * The certificate to trust/import. - */ - certificate: Certificate; - /** - * The message to display to the user. - */ - message: string; - } - - interface CertificateVerifyProcRequest { - hostname: string; - certificate: Certificate; - /** - * Verification result from chromium. - */ - verificationResult: string; - /** - * Error code. - */ - errorCode: number; - } - - interface ClearStorageDataOptions { - /** - * Should follow window.location.origin’s representation scheme://host:port. - */ - origin?: string; - /** - * The types of storages to clear, can contain: appcache, cookies, filesystem, - * indexdb, localstorage, shadercache, websql, serviceworkers, cachestorage. - */ - storages?: string[]; - /** - * The types of quotas to clear, can contain: temporary, persistent, syncable. - */ - quotas?: string[]; - } - - interface CommandLine { - /** - * Append a switch (with optional value) to Chromium's command line. Note: This - * will not affect process.argv. The intended usage of this function is to control - * Chromium's behavior. - */ - appendSwitch: (the_switch: string, value?: string) => void; - /** - * Append an argument to Chromium's command line. The argument will be quoted - * correctly. Switches will precede arguments regardless of appending order. If - * you're appending an argument like --switch=value, consider using - * appendSwitch('switch', 'value') instead. Note: This will not affect - * process.argv. The intended usage of this function is to control Chromium's - * behavior. - */ - appendArgument: (value: string) => void; - hasSwitch: (the_switch: string) => boolean; - /** - * Note: When the switch is not present or has no value, it returns empty string. - */ - getSwitchValue: (the_switch: string) => string; - } - - interface Config { - /** - * The URL associated with the PAC file. - */ - pacScript: string; - /** - * Rules indicating which proxies to use. - */ - proxyRules: string; - /** - * Rules indicating which URLs should bypass the proxy settings. - */ - proxyBypassRules: string; - } - - interface ConsoleMessageEvent extends Event { - level: number; - message: string; - line: number; - sourceId: string; - } - - interface ContextMenuParams { - /** - * x coordinate. - */ - x: number; - /** - * y coordinate. - */ - y: number; - /** - * URL of the link that encloses the node the context menu was invoked on. - */ - linkURL: string; - /** - * Text associated with the link. May be an empty string if the contents of the - * link are an image. - */ - linkText: string; - /** - * URL of the top level page that the context menu was invoked on. - */ - pageURL: string; - /** - * URL of the subframe that the context menu was invoked on. - */ - frameURL: string; - /** - * Source URL for the element that the context menu was invoked on. Elements with - * source URLs are images, audio and video. - */ - srcURL: string; - /** - * Type of the node the context menu was invoked on. Can be none, image, audio, - * video, canvas, file or plugin. - */ - mediaType: ('none' | 'image' | 'audio' | 'video' | 'canvas' | 'file' | 'plugin'); - /** - * Whether the context menu was invoked on an image which has non-empty contents. - */ - hasImageContents: boolean; - /** - * Whether the context is editable. - */ - isEditable: boolean; - /** - * Text of the selection that the context menu was invoked on. - */ - selectionText: string; - /** - * Title or alt text of the selection that the context was invoked on. - */ - titleText: string; - /** - * The misspelled word under the cursor, if any. - */ - misspelledWord: string; - /** - * The character encoding of the frame on which the menu was invoked. - */ - frameCharset: string; - /** - * If the context menu was invoked on an input field, the type of that field. - * Possible values are none, plainText, password, other. - */ - inputFieldType: string; - /** - * Input source that invoked the context menu. Can be none, mouse, keyboard, touch - * or touchMenu. - */ - menuSourceType: ('none' | 'mouse' | 'keyboard' | 'touch' | 'touchMenu'); - /** - * The flags for the media element the context menu was invoked on. - */ - mediaFlags: MediaFlags; - /** - * These flags indicate whether the renderer believes it is able to perform the - * corresponding action. - */ - editFlags: EditFlags; - } - - interface CrashReporterStartOptions { - companyName: string; - /** - * URL that crash reports will be sent to as POST. - */ - submitURL: string; - /** - * Defaults to app.getName(). - */ - productName?: string; - /** - * Whether crash reports should be sent to the server Default is true. - */ - uploadToServer?: boolean; - /** - * Default is false. - */ - ignoreSystemCrashHandler?: boolean; - /** - * An object you can define that will be sent along with the report. Only string - * properties are sent correctly. Nested objects are not supported and the property - * names and values must be less than 64 characters long. - */ - extra?: Extra; - /** - * Directory to store the crashreports temporarily (only used when the crash - * reporter is started via process.crashReporter.start). - */ - crashesDirectory?: string; - } - - interface CreateFromBitmapOptions { - width: number; - height: number; - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface CreateFromBufferOptions { - /** - * Required for bitmap buffers. - */ - width?: number; - /** - * Required for bitmap buffers. - */ - height?: number; - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface CreateInterruptedDownloadOptions { - /** - * Absolute path of the download. - */ - path: string; - /** - * Complete URL chain for the download. - */ - urlChain: string[]; - mimeType?: string; - /** - * Start range for the download. - */ - offset: number; - /** - * Total length of the download. - */ - length: number; - /** - * Last-Modified header value. - */ - lastModified: string; - /** - * ETag header value. - */ - eTag: string; - /** - * Time when download was started in number of seconds since UNIX epoch. - */ - startTime?: number; - } - - interface Data { - text?: string; - html?: string; - image?: NativeImage; - rtf?: string; - /** - * The title of the URL at text. - */ - bookmark?: string; - } - - interface Details { - /** - * The url to associate the cookie with. - */ - url: string; - /** - * The name of the cookie. Empty by default if omitted. - */ - name?: string; - /** - * The value of the cookie. Empty by default if omitted. - */ - value?: string; - /** - * The domain of the cookie. Empty by default if omitted. - */ - domain?: string; - /** - * The path of the cookie. Empty by default if omitted. - */ - path?: string; - /** - * Whether the cookie should be marked as Secure. Defaults to false. - */ - secure?: boolean; - /** - * Whether the cookie should be marked as HTTP only. Defaults to false. - */ - httpOnly?: boolean; - /** - * The expiration date of the cookie as the number of seconds since the UNIX epoch. - * If omitted then the cookie becomes a session cookie and will not be retained - * between sessions. - */ - expirationDate?: number; - } - - interface DevToolsExtensions { - } - - interface DidChangeThemeColorEvent extends Event { - themeColor: string; - } - - interface DidFailLoadEvent extends Event { - errorCode: number; - errorDescription: string; - validatedURL: string; - isMainFrame: boolean; - } - - interface DidFrameFinishLoadEvent extends Event { - isMainFrame: boolean; - } - - interface DidNavigateEvent extends Event { - url: string; - } - - interface DidNavigateInPageEvent extends Event { - isMainFrame: boolean; - url: string; - } - - interface DisplayBalloonOptions { - /** - * - - */ - icon?: (NativeImage) | (string); - title: string; - content: string; - } - - interface Dock { - /** - * When critical is passed, the dock icon will bounce until either the application - * becomes active or the request is canceled. When informational is passed, the - * dock icon will bounce for one second. However, the request remains active until - * either the application becomes active or the request is canceled. Nota Bene: - * This method can only be used while the app is not focused; when the app is - * focused it will return -1. - */ - bounce: (type?: 'critical' | 'informational') => number; - /** - * Cancel the bounce of id. - */ - cancelBounce: (id: number) => void; - /** - * Bounces the Downloads stack if the filePath is inside the Downloads folder. - */ - downloadFinished: (filePath: string) => void; - /** - * Sets the string to be displayed in the dock’s badging area. - */ - setBadge: (text: string) => void; - getBadge: () => string; - /** - * Hides the dock icon. - */ - hide: () => void; - show: () => Promise; - isVisible: () => boolean; - /** - * Sets the application's dock menu. - */ - setMenu: (menu: Menu) => void; - getMenu: () => (Menu) | (null); - /** - * Sets the image associated with this dock icon. - */ - setIcon: (image: (NativeImage) | (string)) => void; - } - - interface EnableNetworkEmulationOptions { - /** - * Whether to emulate network outage. Defaults to false. - */ - offline?: boolean; - /** - * RTT in ms. Defaults to 0 which will disable latency throttling. - */ - latency?: number; - /** - * Download rate in Bps. Defaults to 0 which will disable download throttling. - */ - downloadThroughput?: number; - /** - * Upload rate in Bps. Defaults to 0 which will disable upload throttling. - */ - uploadThroughput?: number; - } - - interface Extensions { - } - - interface FeedURLOptions { - url: string; - /** - * HTTP request headers. - */ - headers?: Headers; - /** - * Either json or default, see the README for more information. - */ - serverType?: string; - } - - interface FileIconOptions { - size: ('small' | 'normal' | 'large'); - } - - interface Filter { - /** - * Retrieves cookies which are associated with url. Empty implies retrieving - * cookies of all urls. - */ - url?: string; - /** - * Filters cookies by name. - */ - name?: string; - /** - * Retrieves cookies whose domains match or are subdomains of domains. - */ - domain?: string; - /** - * Retrieves cookies whose path matches path. - */ - path?: string; - /** - * Filters cookies by their Secure property. - */ - secure?: boolean; - /** - * Filters out session or persistent cookies. - */ - session?: boolean; - } - - interface FindInPageOptions { - /** - * Whether to search forward or backward, defaults to true. - */ - forward?: boolean; - /** - * Whether the operation is first request or a follow up, defaults to false. - */ - findNext?: boolean; - /** - * Whether search should be case-sensitive, defaults to false. - */ - matchCase?: boolean; - /** - * Whether to look only at the start of words. defaults to false. - */ - wordStart?: boolean; - /** - * When combined with wordStart, accepts a match in the middle of a word if the - * match begins with an uppercase letter followed by a lowercase or non-letter. - * Accepts several other intra-word matches, defaults to false. - */ - medialCapitalAsWordStart?: boolean; - } - - interface FoundInPageEvent extends Event { - result: FoundInPageResult; - } - - interface FromPartitionOptions { - /** - * Whether to enable cache. - */ - cache: boolean; - } - - interface Header { - /** - * Specify an extra header name. - */ - name: string; - } - - interface Headers { - } - - interface HeapStatistics { - totalHeapSize: number; - totalHeapSizeExecutable: number; - totalPhysicalSize: number; - totalAvailableSize: number; - usedHeapSize: number; - heapSizeLimit: number; - mallocedMemory: number; - peakMallocedMemory: number; - doesZapGarbage: boolean; - } - - interface IgnoreMouseEventsOptions { - /** - * If true, forwards mouse move messages to Chromium, enabling mouse related events - * such as mouseleave. Only used when ignore is true. If ignore is false, - * forwarding is always disabled regardless of this value. - */ - forward?: boolean; - } - - interface ImportCertificateOptions { - /** - * Path for the pkcs12 file. - */ - certificate: string; - /** - * Passphrase for the certificate. - */ - password: string; - } - - interface Info { - /** - * Security origin for the isolated world. - */ - securityOrigin?: string; - /** - * Content Security Policy for the isolated world. - */ - csp?: string; - /** - * Name for isolated world. Useful in devtools. - */ - name?: string; - } - - interface Input { - /** - * Either keyUp or keyDown. - */ - type: string; - /** - * Equivalent to . - */ - key: string; - /** - * Equivalent to . - */ - code: string; - /** - * Equivalent to . - */ - isAutoRepeat: boolean; - /** - * Equivalent to . - */ - shift: boolean; - /** - * Equivalent to . - */ - control: boolean; - /** - * Equivalent to . - */ - alt: boolean; - /** - * Equivalent to . - */ - meta: boolean; - } - - interface InterceptBufferProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface InterceptFileProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface InterceptHttpProtocolRequest { - url: string; - headers: Headers; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface InterceptStreamProtocolRequest { - url: string; - headers: Headers; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface InterceptStringProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface IpcMessageEvent extends Event { - channel: string; - args: any[]; - } - - interface Item { - /** - * or files Array The path(s) to the file(s) being dragged. - */ - file: string; - /** - * The image must be non-empty on macOS. - */ - icon: NativeImage; - } - - interface JumpListSettings { - /** - * The minimum number of items that will be shown in the Jump List (for a more - * detailed description of this value see the ). - */ - minItems: number; - /** - * Array of JumpListItem objects that correspond to items that the user has - * explicitly removed from custom categories in the Jump List. These items must not - * be re-added to the Jump List in the call to app.setJumpList(), Windows will not - * display any custom category that contains any of the removed items. - */ - removedItems: JumpListItem[]; - } - - interface LoadCommitEvent extends Event { - url: string; - isMainFrame: boolean; - } - - interface LoadFileOptions { - /** - * Passed to url.format(). - */ - query?: Query; - /** - * Passed to url.format(). - */ - search?: string; - /** - * Passed to url.format(). - */ - hash?: string; - } - - interface LoadURLOptions { - /** - * An HTTP Referrer url. - */ - httpReferrer?: (string) | (Referrer); - /** - * A user agent originating the request. - */ - userAgent?: string; - /** - * Extra headers separated by "\n" - */ - extraHeaders?: string; - postData?: (UploadRawData[]) | (UploadFile[]) | (UploadBlob[]); - /** - * Base url (with trailing path separator) for files to be loaded by the data url. - * This is needed only if the specified url is a data url and needs to load other - * files. - */ - baseURLForDataURL?: string; - } - - interface LoginItemSettings { - options?: Options; - /** - * true if the app is set to open at login. - */ - openAtLogin: boolean; - /** - * true if the app is set to open as hidden at login. This setting is not available - * on . - */ - openAsHidden: boolean; - /** - * true if the app was opened at login automatically. This setting is not available - * on . - */ - wasOpenedAtLogin: boolean; - /** - * true if the app was opened as a hidden login item. This indicates that the app - * should not open any windows at startup. This setting is not available on . - */ - wasOpenedAsHidden: boolean; - /** - * true if the app was opened as a login item that should restore the state from - * the previous session. This indicates that the app should restore the windows - * that were open the last time the app was closed. This setting is not available - * on . - */ - restoreState: boolean; - } - - interface LoginItemSettingsOptions { - /** - * The executable path to compare against. Defaults to process.execPath. - */ - path?: string; - /** - * The command-line arguments to compare against. Defaults to an empty array. - */ - args?: string[]; - } - - interface MemoryDumpConfig { - } - - interface MenuItemConstructorOptions { - /** - * Will be called with click(menuItem, browserWindow, event) when the menu item is - * clicked. - */ - click?: (menuItem: MenuItem, browserWindow: BrowserWindow, event: KeyboardEvent) => void; - /** - * Can be undo, redo, cut, copy, paste, pasteAndMatchStyle, delete, selectAll, - * reload, forceReload, toggleDevTools, resetZoom, zoomIn, zoomOut, - * togglefullscreen, window, minimize, close, help, about, services, hide, - * hideOthers, unhide, quit, startSpeaking, stopSpeaking, close, minimize, zoom, - * front, appMenu, fileMenu, editMenu, viewMenu, recentDocuments, toggleTabBar, - * selectNextTab, selectPreviousTab, mergeAllWindows, clearRecentDocuments, - * moveTabToNewWindow or windowMenu Define the action of the menu item, when - * specified the click property will be ignored. See . - */ - role?: ('undo' | 'redo' | 'cut' | 'copy' | 'paste' | 'pasteAndMatchStyle' | 'delete' | 'selectAll' | 'reload' | 'forceReload' | 'toggleDevTools' | 'resetZoom' | 'zoomIn' | 'zoomOut' | 'togglefullscreen' | 'window' | 'minimize' | 'close' | 'help' | 'about' | 'services' | 'hide' | 'hideOthers' | 'unhide' | 'quit' | 'startSpeaking' | 'stopSpeaking' | 'close' | 'minimize' | 'zoom' | 'front' | 'appMenu' | 'fileMenu' | 'editMenu' | 'viewMenu' | 'recentDocuments' | 'toggleTabBar' | 'selectNextTab' | 'selectPreviousTab' | 'mergeAllWindows' | 'clearRecentDocuments' | 'moveTabToNewWindow' | 'windowMenu'); - /** - * Can be normal, separator, submenu, checkbox or radio. - */ - type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'); - label?: string; - sublabel?: string; - accelerator?: Accelerator; - icon?: (NativeImage) | (string); - /** - * If false, the menu item will be greyed out and unclickable. - */ - enabled?: boolean; - /** - * default is true, and when false will prevent the accelerator from triggering the - * item if the item is not visible`. - */ - acceleratorWorksWhenHidden?: boolean; - /** - * If false, the menu item will be entirely hidden. - */ - visible?: boolean; - /** - * Should only be specified for checkbox or radio type menu items. - */ - checked?: boolean; - /** - * If false, the accelerator won't be registered with the system, but it will still - * be displayed. Defaults to true. - */ - registerAccelerator?: boolean; - /** - * Should be specified for submenu type menu items. If submenu is specified, the - * type: 'submenu' can be omitted. If the value is not a then it will be - * automatically converted to one using Menu.buildFromTemplate. - */ - submenu?: (MenuItemConstructorOptions[]) | (Menu); - /** - * Unique within a single menu. If defined then it can be used as a reference to - * this item by the position attribute. - */ - id?: string; - /** - * Inserts this item before the item with the specified label. If the referenced - * item doesn't exist the item will be inserted at the end of the menu. Also - * implies that the menu item in question should be placed in the same “group” as - * the item. - */ - before?: string[]; - /** - * Inserts this item after the item with the specified label. If the referenced - * item doesn't exist the item will be inserted at the end of the menu. - */ - after?: string[]; - /** - * Provides a means for a single context menu to declare the placement of their - * containing group before the containing group of the item with the specified - * label. - */ - beforeGroupContaining?: string[]; - /** - * Provides a means for a single context menu to declare the placement of their - * containing group after the containing group of the item with the specified - * label. - */ - afterGroupContaining?: string[]; - } - - 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. The - * checkbox state can be inspected only when using callback. - */ - 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 or callback response. - */ - 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; - } - - interface MessageBoxReturnValue { - /** - * The index of the clicked button. - */ - response: number; - /** - * The checked state of the checkbox if checkboxLabel was set. Otherwise false. - */ - checkboxChecked: boolean; - } - - interface MessageBoxSyncOptions { - /** - * 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. The - * checkbox state can be inspected only when using callback. - */ - checkboxLabel?: string; - /** - * Initial checked state of the checkbox. false by default. - */ - checkboxChecked?: boolean; - icon?: (NativeImage) | (string); - /** - * 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 or callback response. - */ - 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; - } - - interface NewWindowEvent extends Event { - url: string; - frameName: string; - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'); - /** - * The options which should be used for creating the new . - */ - options: Options; - } - - interface NotificationConstructorOptions { - /** - * A title for the notification, which will be shown at the top of the notification - * window when it is shown. - */ - title: string; - /** - * A subtitle for the notification, which will be displayed below the title. - */ - subtitle?: string; - /** - * The body text of the notification, which will be displayed below the title or - * subtitle. - */ - body: string; - /** - * Whether or not to emit an OS notification noise when showing the notification. - */ - silent?: boolean; - /** - * An icon to use in the notification. - */ - icon?: (string) | (NativeImage); - /** - * Whether or not to add an inline reply option to the notification. - */ - hasReply?: boolean; - /** - * The placeholder to write in the inline reply input field. - */ - replyPlaceholder?: string; - /** - * The name of the sound file to play when the notification is shown. - */ - sound?: string; - /** - * Actions to add to the notification. Please read the available actions and - * limitations in the NotificationAction documentation. - */ - actions?: NotificationAction[]; - /** - * A custom title for the close button of an alert. An empty string will cause the - * default localized text to be used. - */ - closeButtonText?: string; - } - - interface OnBeforeRedirectDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - redirectURL: string; - statusCode: number; - /** - * The server IP address that the request was actually sent to. - */ - ip?: string; - fromCache: boolean; - responseHeaders: ResponseHeaders; - } - - interface OnBeforeRedirectFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnBeforeRequestDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - uploadData: UploadData[]; - } - - interface OnBeforeRequestFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnBeforeSendHeadersDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - requestHeaders: RequestHeaders; - } - - interface OnBeforeSendHeadersFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnBeforeSendHeadersResponse { - cancel?: boolean; - /** - * When provided, request will be made with these headers. - */ - requestHeaders?: RequestHeaders; - } - - interface OnCompletedDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - responseHeaders: ResponseHeaders; - fromCache: boolean; - statusCode: number; - statusLine: string; - } - - interface OnCompletedFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnErrorOccurredDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - fromCache: boolean; - /** - * The error description. - */ - error: string; - } - - interface OnErrorOccurredFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnHeadersReceivedDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - statusLine: string; - statusCode: number; - responseHeaders: ResponseHeaders; - } - - interface OnHeadersReceivedFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnHeadersReceivedResponse { - cancel?: boolean; - /** - * When provided, the server is assumed to have responded with these headers. - */ - responseHeaders?: ResponseHeaders; - /** - * Should be provided when overriding responseHeaders to change header status - * otherwise original response header's status will be used. - */ - statusLine?: string; - } - - interface OnResponseStartedDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - responseHeaders: ResponseHeaders; - /** - * Indicates whether the response was fetched from disk cache. - */ - fromCache: boolean; - statusCode: number; - statusLine: string; - } - - interface OnResponseStartedFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnSendHeadersDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - requestHeaders: RequestHeaders; - } - - interface OnSendHeadersFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - 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; - } - - 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'>; - /** - * Message to display above input boxes. - */ - message?: string; - /** - * Create when packaged for the Mac App Store. - */ - securityScopedBookmarks?: boolean; - } - - 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. - */ - bookmarks?: string[]; - } - - interface OpenDialogSyncOptions { - 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'>; - /** - * Message to display above input boxes. - */ - message?: string; - /** - * Create when packaged for the Mac App Store. - */ - securityScopedBookmarks?: boolean; - } - - interface OpenExternalOptions { - /** - * true to bring the opened application to the foreground. The default is true. - */ - activate?: boolean; - /** - * The working directory. - */ - workingDirectory?: string; - } - - interface OpenExternalSyncOptions { - /** - * true to bring the opened application to the foreground. The default is true. - */ - activate?: boolean; - /** - * The working directory. - */ - workingDirectory?: string; - } - - interface PageFaviconUpdatedEvent extends Event { - /** - * Array of URLs. - */ - favicons: string[]; - } - - interface PageTitleUpdatedEvent extends Event { - title: string; - explicitSet: boolean; - } - - interface Parameters { - /** - * Specify the screen type to emulate (default: desktop): - */ - screenPosition: ('desktop' | 'mobile'); - /** - * Set the emulated screen size (screenPosition == mobile). - */ - screenSize: Size; - /** - * Position the view on the screen (screenPosition == mobile) (default: { x: 0, y: - * 0 }). - */ - viewPosition: Point; - /** - * Set the device scale factor (if zero defaults to original device scale factor) - * (default: 0). - */ - deviceScaleFactor: number; - /** - * Set the emulated view size (empty means no override) - */ - viewSize: Size; - /** - * Scale of emulated view inside available space (not in fit to view mode) - * (default: 1). - */ - scale: number; - } - - interface Payment { - /** - * The identifier of the purchased product. - */ - productIdentifier: string; - /** - * The quantity purchased. - */ - quantity: number; - } - - interface PermissionCheckHandlerDetails { - /** - * The security orign of the media check. - */ - securityOrigin: string; - /** - * The type of media access being requested, can be video, audio or unknown - */ - mediaType: ('video' | 'audio' | 'unknown'); - /** - * The last URL the requesting frame loaded - */ - requestingUrl: string; - /** - * Whether the frame making the request is the main frame - */ - isMainFrame: boolean; - } - - interface PermissionRequestHandlerDetails { - /** - * The url of the openExternal request. - */ - externalURL?: string; - /** - * The types of media access being requested, elements can be video or audio - */ - mediaTypes?: Array<'video' | 'audio'>; - /** - * The last URL the requesting frame loaded - */ - requestingUrl: string; - /** - * Whether the frame making the request is the main frame - */ - isMainFrame: boolean; - } - - interface PluginCrashedEvent extends Event { - name: string; - version: string; - } - - interface PopupOptions { - /** - * Default is the focused window. - */ - window?: BrowserWindow; - /** - * Default is the current mouse cursor position. Must be declared if y is declared. - */ - x?: number; - /** - * Default is the current mouse cursor position. Must be declared if x is declared. - */ - y?: number; - /** - * The index of the menu item to be positioned under the mouse cursor at the - * specified coordinates. Default is -1. - */ - positioningItem?: number; - /** - * Called when menu is closed. - */ - callback?: () => void; - } - - interface PrintOptions { - /** - * Don't ask user for print settings. Default is false. - */ - silent?: boolean; - /** - * Also prints the background color and image of the web page. Default is false. - */ - printBackground?: boolean; - /** - * Set the printer device name to use. Default is ''. - */ - deviceName?: string; - } - - interface PrintToPDFOptions { - /** - * Specifies the type of margins to use. Uses 0 for default margin, 1 for no - * margin, and 2 for minimum margin. - */ - marginsType?: number; - /** - * Specify page size of the generated PDF. Can be A3, A4, A5, Legal, Letter, - * Tabloid or an Object containing height and width in microns. - */ - pageSize?: (string) | (Size); - /** - * Whether to print CSS backgrounds. - */ - printBackground?: boolean; - /** - * Whether to print selection only. - */ - printSelectionOnly?: boolean; - /** - * true for landscape, false for portrait. - */ - landscape?: boolean; - } - - interface Privileges { - /** - * Default false. - */ - standard?: boolean; - /** - * Default false. - */ - secure?: boolean; - /** - * Default false. - */ - bypassCSP?: boolean; - /** - * Default false. - */ - allowServiceWorkers?: boolean; - /** - * Default false. - */ - supportFetchAPI?: boolean; - /** - * Default false. - */ - corsEnabled?: boolean; - } - - interface ProgressBarOptions { - /** - * Mode for the progress bar. Can be none, normal, indeterminate, error or paused. - */ - mode: ('none' | 'normal' | 'indeterminate' | 'error' | 'paused'); - } - - interface Provider { - /** - * . - */ - spellCheck: (words: string[], callback: (misspeltWords: string[]) => void) => void; - } - - interface ReadBookmark { - title: string; - url: string; - } - - interface RedirectRequest { - url: string; - method: string; - session?: Session; - uploadData?: UploadData; - } - - interface RegisterBufferProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RegisterFileProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RegisterHttpProtocolRequest { - url: string; - headers: Headers; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RegisterStreamProtocolRequest { - url: string; - headers: Headers; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RegisterStringProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RelaunchOptions { - args?: string[]; - execPath?: string; - } - - interface Request { - method: string; - url: string; - referrer: string; - } - - interface ResizeOptions { - /** - * Defaults to the image's width. - */ - width?: number; - /** - * Defaults to the image's height. - */ - height?: number; - /** - * The desired quality of the resize image. Possible values are good, better or - * best. The default is best. These values express a desired quality/speed - * tradeoff. They are translated into an algorithm-specific method that depends on - * the capabilities (CPU, GPU) of the underlying platform. It is possible for all - * three methods to be mapped to the same algorithm on a given platform. - */ - quality?: string; - } - - interface ResourceUsage { - images: MemoryUsageDetails; - scripts: MemoryUsageDetails; - cssStyleSheets: MemoryUsageDetails; - xslStyleSheets: MemoryUsageDetails; - fonts: MemoryUsageDetails; - other: MemoryUsageDetails; - } - - interface Response { - cancel?: boolean; - /** - * The original request is prevented from being sent or completed and is instead - * redirected to the given URL. - */ - redirectURL?: string; - } - - interface Result { - requestId: number; - /** - * Position of the active match. - */ - activeMatchOrdinal: number; - /** - * Number of Matches. - */ - matches: number; - /** - * Coordinates of first match region. - */ - selectionArea: SelectionArea; - finalUpdate: boolean; - } - - 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. - */ - message?: string; - /** - * Custom label for the text displayed in front of the filename text field. - */ - nameFieldLabel?: string; - /** - * Show the tags input box, defaults to true. - */ - showsTagField?: boolean; - /** - * Create a 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. - */ - securityScopedBookmarks?: boolean; - } - - 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. - */ - bookmark?: string; - } - - interface SaveDialogSyncOptions { - 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. - */ - message?: string; - /** - * Custom label for the text displayed in front of the filename text field. - */ - nameFieldLabel?: string; - /** - * Show the tags input box, defaults to true. - */ - showsTagField?: boolean; - /** - * Create a 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. - */ - securityScopedBookmarks?: boolean; - } - - interface Settings { - /** - * true to open the app at login, false to remove the app as a login item. Defaults - * to false. - */ - openAtLogin?: boolean; - /** - * true to open the app as hidden. Defaults to false. The user can edit this - * setting from the System Preferences so - * app.getLoginItemSettings().wasOpenedAsHidden should be checked when the app is - * opened to know the current value. This setting is not available on . - */ - openAsHidden?: boolean; - /** - * The executable to launch at login. Defaults to process.execPath. - */ - path?: string; - /** - * The command-line arguments to pass to the executable. Defaults to an empty - * array. Take care to wrap paths in quotes. - */ - args?: string[]; - } - - interface SourcesOptions { - /** - * An array of Strings that lists the types of desktop sources to be captured, - * available types are screen and window. - */ - types: string[]; - /** - * The size that the media source thumbnail should be scaled to. Default is 150 x - * 150. Set width or height to 0 when you do not need the thumbnails. This will - * save the processing time required for capturing the content of each window and - * screen. - */ - thumbnailSize?: Size; - /** - * Set to true to enable fetching window icons. The default value is false. When - * false the appIcon property of the sources return null. Same if a source has the - * type screen. - */ - fetchWindowIcons?: boolean; - } - - interface SystemMemoryInfo { - /** - * The total amount of physical memory in Kilobytes available to the system. - */ - total: number; - /** - * The total amount of memory not being used by applications or disk cache. - */ - free: number; - /** - * The total amount of swap memory in Kilobytes available to the system. - */ - swapTotal: number; - /** - * The free amount of swap memory in Kilobytes available to the system. - */ - swapFree: number; - } - - interface ToBitmapOptions { - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface ToDataURLOptions { - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface ToPNGOptions { - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface TouchBarButtonConstructorOptions { - /** - * Button text. - */ - label?: string; - /** - * Button background color in hex format, i.e #ABCDEF. - */ - backgroundColor?: string; - /** - * Button icon. - */ - icon?: NativeImage; - /** - * Can be left, right or overlay. - */ - iconPosition?: ('left' | 'right' | 'overlay'); - /** - * Function to call when the button is clicked. - */ - click?: () => void; - } - - interface TouchBarColorPickerConstructorOptions { - /** - * Array of hex color strings to appear as possible colors to select. - */ - availableColors?: string[]; - /** - * The selected hex color in the picker, i.e #ABCDEF. - */ - selectedColor?: string; - /** - * Function to call when a color is selected. - */ - change?: (color: string) => void; - } - - interface TouchBarConstructorOptions { - items: Array<(TouchBarButton) | (TouchBarColorPicker) | (TouchBarGroup) | (TouchBarLabel) | (TouchBarPopover) | (TouchBarScrubber) | (TouchBarSegmentedControl) | (TouchBarSlider) | (TouchBarSpacer)>; - escapeItem?: (TouchBarButton) | (TouchBarColorPicker) | (TouchBarGroup) | (TouchBarLabel) | (TouchBarPopover) | (TouchBarScrubber) | (TouchBarSegmentedControl) | (TouchBarSlider) | (TouchBarSpacer) | (null); - } - - interface TouchBarGroupConstructorOptions { - /** - * Items to display as a group. - */ - items: TouchBar; - } - - interface TouchBarLabelConstructorOptions { - /** - * Text to display. - */ - label?: string; - /** - * Hex color of text, i.e #ABCDEF. - */ - textColor?: string; - } - - interface TouchBarPopoverConstructorOptions { - /** - * Popover button text. - */ - label?: string; - /** - * Popover button icon. - */ - icon?: NativeImage; - /** - * Items to display in the popover. - */ - items?: TouchBar; - /** - * true to display a close button on the left of the popover, false to not show it. - * Default is true. - */ - showCloseButton?: boolean; - } - - interface TouchBarScrubberConstructorOptions { - /** - * An array of items to place in this scrubber. - */ - items: ScrubberItem[]; - /** - * Called when the user taps an item that was not the last tapped item. - */ - select: (selectedIndex: number) => void; - /** - * Called when the user taps any item. - */ - highlight: (highlightedIndex: number) => void; - /** - * Selected item style. Defaults to null. - */ - selectedStyle: string; - /** - * Selected overlay item style. Defaults to null. - */ - overlayStyle: string; - /** - * Defaults to false. - */ - showArrowButtons: boolean; - /** - * Defaults to free. - */ - mode: string; - /** - * Defaults to true. - */ - continuous: boolean; - } - - interface TouchBarSegmentedControlConstructorOptions { - /** - * Style of the segments: - */ - segmentStyle?: ('automatic' | 'rounded' | 'textured-rounded' | 'round-rect' | 'textured-square' | 'capsule' | 'small-square' | 'separated'); - /** - * The selection mode of the control: - */ - mode?: ('single' | 'multiple' | 'buttons'); - /** - * An array of segments to place in this control. - */ - segments: SegmentedControlSegment[]; - /** - * The index of the currently selected segment, will update automatically with user - * interaction. When the mode is multiple it will be the last selected item. - */ - selectedIndex?: number; - /** - * Called when the user selects a new segment. - */ - change: (selectedIndex: number, isSelected: boolean) => void; - } - - interface TouchBarSliderConstructorOptions { - /** - * Label text. - */ - label?: string; - /** - * Selected value. - */ - value?: number; - /** - * Minimum value. - */ - minValue?: number; - /** - * Maximum value. - */ - maxValue?: number; - /** - * Function to call when the slider is changed. - */ - change?: (newValue: number) => void; - } - - interface TouchBarSpacerConstructorOptions { - /** - * Size of spacer, possible values are: - */ - size?: ('small' | 'large' | 'flexible'); - } - - interface UpdateTargetUrlEvent extends Event { - url: string; - } - - interface UploadProgress { - /** - * Whether the request is currently active. If this is false no other properties - * will be set - */ - active: boolean; - /** - * Whether the upload has started. If this is false both current and total will be - * set to 0. - */ - started: boolean; - /** - * The number of bytes that have been uploaded so far - */ - current: number; - /** - * The number of bytes that will be uploaded this request - */ - total: number; - } - - interface Versions { - /** - * A String representing Chrome's version string. - */ - chrome?: string; - /** - * A String representing Electron's version string. - */ - electron?: string; - } - - interface VisibleOnAllWorkspacesOptions { - /** - * Sets whether the window should be visible above fullscreen windows - */ - visibleOnFullScreen?: boolean; - } - - interface WillNavigateEvent extends Event { - url: string; - } - - interface EditFlags { - /** - * Whether the renderer believes it can undo. - */ - canUndo: boolean; - /** - * Whether the renderer believes it can redo. - */ - canRedo: boolean; - /** - * Whether the renderer believes it can cut. - */ - canCut: boolean; - /** - * Whether the renderer believes it can copy - */ - canCopy: boolean; - /** - * Whether the renderer believes it can paste. - */ - canPaste: boolean; - /** - * Whether the renderer believes it can delete. - */ - canDelete: boolean; - /** - * Whether the renderer believes it can select all. - */ - canSelectAll: boolean; - } - - interface Extra { - } - - interface FoundInPageResult { - requestId: number; - /** - * Position of the active match. - */ - activeMatchOrdinal: number; - /** - * Number of Matches. - */ - matches: number; - /** - * Coordinates of first match region. - */ - selectionArea: SelectionArea; - finalUpdate: boolean; - } - - interface MediaFlags { - /** - * Whether the media element has crashed. - */ - inError: boolean; - /** - * Whether the media element is paused. - */ - isPaused: boolean; - /** - * Whether the media element is muted. - */ - isMuted: boolean; - /** - * Whether the media element has audio. - */ - hasAudio: boolean; - /** - * Whether the media element is looping. - */ - isLooping: boolean; - /** - * Whether the media element's controls are visible. - */ - isControlsVisible: boolean; - /** - * Whether the media element's controls are toggleable. - */ - canToggleControls: boolean; - /** - * Whether the media element can be rotated. - */ - canRotate: boolean; - } - - interface Options { - } - - interface Query { - } - - interface RequestHeaders { - } - - interface ResponseHeaders { - } - - interface SelectionArea { - } - - interface WebPreferences { - /** - * Whether to enable DevTools. If it is set to false, can not use - * BrowserWindow.webContents.openDevTools() to open DevTools. Default is true. - */ - devTools?: boolean; - /** - * Whether node integration is enabled. Default is false. - */ - nodeIntegration?: boolean; - /** - * Whether node integration is enabled in web workers. Default is false. More about - * this can be found in . - */ - nodeIntegrationInWorker?: boolean; - /** - * Experimental option for enabling Node.js support in sub-frames such as iframes - * and child windows. All your preloads will load for every iframe, you can use - * process.isMainFrame to determine if you are in the main frame or not. - */ - nodeIntegrationInSubFrames?: boolean; - /** - * Specifies a script that will be loaded before other scripts run in the page. - * This script will always have access to node APIs no matter whether node - * integration is turned on or off. The value should be the absolute file path to - * the script. When node integration is turned off, the preload script can - * reintroduce Node global symbols back to the global scope. See example . - */ - preload?: string; - /** - * If set, this will sandbox the renderer associated with the window, making it - * compatible with the Chromium OS-level sandbox and disabling the Node.js engine. - * This is not the same as the nodeIntegration option and the APIs available to the - * preload script are more limited. Read more about the option . This option is - * currently experimental and may change or be removed in future Electron releases. - */ - sandbox?: boolean; - /** - * Whether to enable the module. Default is true. - */ - enableRemoteModule?: boolean; - /** - * Sets the session used by the page. Instead of passing the Session object - * directly, you can also choose to use the partition option instead, which accepts - * a partition string. When both session and partition are provided, session will - * be preferred. Default is the default session. - */ - session?: Session; - /** - * Sets the session used by the page according to the session's partition string. - * If partition starts with persist:, the page will use a persistent session - * available to all pages in the app with the same partition. If there is no - * persist: prefix, the page will use an in-memory session. By assigning the same - * partition, multiple pages can share the same session. Default is the default - * session. - */ - partition?: string; - /** - * When specified, web pages with the same affinity will run in the same renderer - * process. Note that due to reusing the renderer process, certain webPreferences - * options will also be shared between the web pages even when you specified - * different values for them, including but not limited to preload, sandbox and - * nodeIntegration. So it is suggested to use exact same webPreferences for web - * pages with the same affinity. - */ - affinity?: string; - /** - * The default zoom factor of the page, 3.0 represents 300%. Default is 1.0. - */ - zoomFactor?: number; - /** - * Enables JavaScript support. Default is true. - */ - javascript?: boolean; - /** - * When false, it will disable the same-origin policy (usually using testing - * websites by people), and set allowRunningInsecureContent to true if this options - * has not been set by user. Default is true. - */ - // webSecurity?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * Allow an https page to run JavaScript, CSS or plugins from http URLs. Default is - * false. - */ - // allowRunningInsecureContent?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * Enables image support. Default is true. - */ - images?: boolean; - /** - * Make TextArea elements resizable. Default is true. - */ - textAreasAreResizable?: boolean; - /** - * Enables WebGL support. Default is true. - */ - webgl?: boolean; - /** - * Whether plugins should be enabled. Default is false. - */ - plugins?: boolean; - /** - * Enables Chromium's experimental features. Default is false. - */ - // experimentalFeatures?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * Enables scroll bounce (rubber banding) effect on macOS. Default is false. - */ - scrollBounce?: boolean; - /** - * A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to - * enable. The full list of supported feature strings can be found in the file. - */ - enableBlinkFeatures?: string; - /** - * A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to - * disable. The full list of supported feature strings can be found in the file. - */ - disableBlinkFeatures?: string; - /** - * Sets the default font for the font-family. - */ - defaultFontFamily?: DefaultFontFamily; - /** - * Defaults to 16. - */ - defaultFontSize?: number; - /** - * Defaults to 13. - */ - defaultMonospaceFontSize?: number; - /** - * Defaults to 0. - */ - minimumFontSize?: number; - /** - * Defaults to ISO-8859-1. - */ - defaultEncoding?: string; - /** - * Whether to throttle animations and timers when the page becomes background. This - * also affects the . Defaults to true. - */ - backgroundThrottling?: boolean; - /** - * Whether to enable offscreen rendering for the browser window. Defaults to false. - * See the for more details. - */ - offscreen?: boolean; - /** - * Whether to run Electron APIs and the specified preload script in a separate - * JavaScript context. Defaults to false. The context that the preload script runs - * in will still have full access to the document and window globals but it will - * use its own set of JavaScript builtins (Array, Object, JSON, etc.) and will be - * isolated from any changes made to the global environment by the loaded page. The - * Electron API will only be available in the preload script and not the loaded - * page. This option should be used when loading potentially untrusted remote - * content to ensure the loaded content cannot tamper with the preload script and - * any Electron APIs being used. This option uses the same technique used by . You - * can access this context in the dev tools by selecting the 'Electron Isolated - * Context' entry in the combo box at the top of the Console tab. - */ - contextIsolation?: boolean; - /** - * Whether to use native window.open(). Defaults to false. Child windows will - * always have node integration disabled unless nodeIntegrationInSubFrames is true. - * This option is currently experimental. - */ - nativeWindowOpen?: boolean; - /** - * Whether to enable the . Defaults to false. The preload script configured for the - * will have node integration enabled when it is executed so you should ensure - * remote/untrusted content is not able to create a tag with a possibly malicious - * preload script. You can use the will-attach-webview event on to strip away the - * preload script and to validate or alter the 's initial settings. - */ - webviewTag?: boolean; - /** - * A list of strings that will be appended to process.argv in the renderer process - * of this app. Useful for passing small bits of data down to renderer process - * preload scripts. - */ - additionalArguments?: string[]; - /** - * Whether to enable browser style consecutive dialog protection. Default is false. - */ - safeDialogs?: boolean; - /** - * The message to display when consecutive dialog protection is triggered. If not - * defined the default message would be used, note that currently the default - * message is in English and not localized. - */ - safeDialogsMessage?: string; - /** - * Whether dragging and dropping a file or link onto the page causes a navigation. - * Default is false. - */ - navigateOnDragDrop?: boolean; - /** - * Autoplay policy to apply to content in the window, can be - * no-user-gesture-required, user-gesture-required, - * document-user-activation-required. Defaults to no-user-gesture-required. - */ - autoplayPolicy?: ('no-user-gesture-required' | 'user-gesture-required' | 'document-user-activation-required'); - /** - * Whether to prevent the window from resizing when entering HTML Fullscreen. - * Default is false. - */ - disableHtmlFullscreenWindowResize?: boolean; - } - - interface DefaultFontFamily { - /** - * Defaults to Times New Roman. - */ - standard?: string; - /** - * Defaults to Times New Roman. - */ - serif?: string; - /** - * Defaults to Arial. - */ - sansSerif?: string; - /** - * Defaults to Courier New. - */ - monospace?: string; - /** - * Defaults to Script. - */ - cursive?: string; - /** - * Defaults to Impact. - */ - fantasy?: string; - } - -} - -declare module 'electron' { - export = Electron; -} - -interface NodeRequireFunction { - (moduleName: 'electron'): typeof Electron; -} - -interface File { - /** - * The real path to the file on the users filesystem - */ - path: string; -} - -declare module 'original-fs' { - import * as fs from 'fs'; - export = fs; -} - -interface Document { - createElement(tagName: 'webview'): Electron.WebviewTag; -} - -declare namespace NodeJS { - interface Process extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/process - - // ### BEGIN VSCODE MODIFICATION ### - // /** - // * Emitted when Electron has loaded its internal initialization script and is - // * beginning to load the web page or the main script. It can be used by the preload - // * script to add removed Node global symbols back to the global scope when node - // * integration is turned off: - // */ - // on(event: 'loaded', listener: Function): this; - // once(event: 'loaded', listener: Function): this; - // addListener(event: 'loaded', listener: Function): this; - // removeListener(event: 'loaded', listener: Function): this; - // ### END VSCODE MODIFICATION ### - /** - * Causes the main thread of the current process crash. - */ - crash(): void; - getCPUUsage(): Electron.CPUUsage; - /** - * Indicates the creation time of the application. The time is represented as - * number of milliseconds since epoch. It returns null if it is unable to get the - * process creation time. - */ - getCreationTime(): (number) | (null); - /** - * Returns an object with V8 heap statistics. Note that all statistics are reported - * in Kilobytes. - */ - getHeapStatistics(): Electron.HeapStatistics; - getIOCounters(): Electron.IOCounters; - /** - * 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; - /** - * Returns an object giving memory usage statistics about the entire system. Note - * that all statistics are reported in Kilobytes. - */ - getSystemMemoryInfo(): Electron.SystemMemoryInfo; - /** - * Examples: Note: It returns the actual operating system version instead of kernel - * version on macOS unlike os.release(). - */ - getSystemVersion(): string; - /** - * Causes the main thread of the current process hang. - */ - hang(): void; - /** - * Sets the file descriptor soft limit to maxDescriptors or the OS hard limit, - * whichever is lower for the current process. - */ - setFdLimit(maxDescriptors: number): void; - /** - * Takes a V8 heap snapshot and saves it to filePath. - */ - takeHeapSnapshot(filePath: string): boolean; - /** - * A Boolean. When app is started by being passed as parameter to the default app, - * this property is true in the main process, otherwise it is undefined. - */ - defaultApp?: boolean; - /** - * A Boolean that controls whether or not deprecation warnings are printed to - * stderr when formerly callback-based APIs converted to Promises are invoked using - * callbacks. Setting this to true will enable deprecation warnings. - */ - enablePromiseAPIs?: boolean; - /** - * A Boolean, true when the current renderer context is the "main" renderer frame. - * If you want the ID of the current frame you should use webFrame.routingId. - */ - isMainFrame?: boolean; - /** - * A Boolean. For Mac App Store build, this property is true, for other builds it - * is undefined. - */ - mas?: boolean; - /** - * A Boolean that controls ASAR support inside your application. Setting this to - * true will disable the support for asar archives in Node's built-in modules. - */ - noAsar?: boolean; - /** - * A Boolean that controls whether or not deprecation warnings are printed to - * stderr. Setting this to true will silence deprecation warnings. This property is - * used instead of the --no-deprecation command line flag. - */ - noDeprecation?: boolean; - /** - * A String representing the path to the resources directory. - */ - resourcesPath?: string; - /** - * A Boolean. When the renderer process is sandboxed, this property is true, - * otherwise it is undefined. - */ - sandboxed?: boolean; - /** - * A Boolean that controls whether or not deprecation warnings will be thrown as - * exceptions. Setting this to true will throw errors for deprecations. This - * property is used instead of the --throw-deprecation command line flag. - */ - throwDeprecation?: boolean; - /** - * A Boolean that controls whether or not deprecations printed to stderr include - * their stack trace. Setting this to true will print stack traces for - * deprecations. This property is instead of the --trace-deprecation command line - * flag. - */ - traceDeprecation?: boolean; - /** - * A Boolean that controls whether or not process warnings printed to stderr - * include their stack trace. Setting this to true will print stack traces for - * process warnings (including deprecations). This property is instead of the - * --trace-warnings command line flag. - */ - traceProcessWarnings?: boolean; - /** - * A String representing the current process's type, can be "browser" (i.e. main - * process), "renderer", or "worker" (i.e. web worker). - */ - type?: string; - /** - * A Boolean. If the app is running as a Windows Store app (appx), this property is - * true, for otherwise it is undefined. - */ - windowsStore?: boolean; - } - interface ProcessVersions { - electron: string; - chrome: string; - } -} diff --git a/src/typings/es2015-proxy.d.ts b/src/typings/es2015-proxy.d.ts deleted file mode 100644 index 00f7c2b06424d..0000000000000 --- 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 2d3271e2848ad..0000000000000 --- 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/http-proxy-agent.d.ts b/src/typings/http-proxy-agent.d.ts deleted file mode 100644 index 3d0071543b488..0000000000000 --- a/src/typings/http-proxy-agent.d.ts +++ /dev/null @@ -1,20 +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 module 'http-proxy-agent' { - - interface IHttpProxyAgentOptions { - host: string; - port: number; - auth?: string; - } - - class HttpProxyAgent { - constructor(proxy: string); - constructor(opts: IHttpProxyAgentOptions); - } - - export = HttpProxyAgent; -} \ No newline at end of file diff --git a/src/typings/jschardet.d.ts b/src/typings/jschardet.d.ts deleted file mode 100644 index f252a47fd094a..0000000000000 --- a/src/typings/jschardet.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -declare module 'jschardet' { - export interface IDetectedMap { - encoding: string, - confidence: number - } - export function detect(buffer: Buffer): IDetectedMap; - - export const Constants: { - MINIMUM_THRESHOLD: number, - } -} \ No newline at end of file diff --git a/src/typings/lib.array-ext.d.ts b/src/typings/lib.array-ext.d.ts deleted file mode 100644 index 5a77b70a9f26e..0000000000000 --- a/src/typings/lib.array-ext.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -interface ArrayConstructor { - isArray(arg: ReadonlyArray | null | undefined): arg is ReadonlyArray; - isArray(arg: Array | null | undefined): arg is Array; - isArray(arg: any): arg is Array; - isArray(arg: any): arg is Array; -} \ No newline at end of file diff --git a/src/typings/lib.es2018.promise.d.ts b/src/typings/lib.es2018.promise.d.ts deleted file mode 100644 index 9f7b2d38cb260..0000000000000 --- 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 43bde6f7c640c..0000000000000 --- 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; - 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; - 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 e84f717c9a415..0000000000000 --- 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/native-is-elevated.d.ts b/src/typings/native-is-elevated.d.ts deleted file mode 100644 index 1b79764d6a056..0000000000000 --- a/src/typings/native-is-elevated.d.ts +++ /dev/null @@ -1,10 +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 module 'native-is-elevated' { - function isElevated(): boolean; - - export = isElevated; -} \ No newline at end of file diff --git a/src/typings/native-keymap.d.ts b/src/typings/native-keymap.d.ts deleted file mode 100644 index 716a74e38b8cc..0000000000000 --- a/src/typings/native-keymap.d.ts +++ /dev/null @@ -1,93 +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 module 'native-keymap' { - - export interface IWindowsKeyMapping { - vkey: string; - value: string; - withShift: string; - withAltGr: string; - withShiftAltGr: string; - } - export interface IWindowsKeyboardMapping { - [code: string]: IWindowsKeyMapping; - } - export interface ILinuxKeyMapping { - value: string; - withShift: string; - withAltGr: string; - withShiftAltGr: string; - } - export interface ILinuxKeyboardMapping { - [code: string]: ILinuxKeyMapping; - } - export interface IMacKeyMapping { - value: string; - withShift: string; - withAltGr: string; - withShiftAltGr: string; - valueIsDeadKey: boolean; - withShiftIsDeadKey: boolean; - withAltGrIsDeadKey: boolean; - withShiftAltGrIsDeadKey: boolean; - } - export interface IMacKeyboardMapping { - [code: string]: IMacKeyMapping; - } - - export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping; - - export function getKeyMap(): IKeyboardMapping; - - /* __GDPR__FRAGMENT__ - "IKeyboardLayoutInfo" : { - "name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - export interface IWindowsKeyboardLayoutInfo { - name: string; - id: string; - text: string; - } - - /* __GDPR__FRAGMENT__ - "IKeyboardLayoutInfo" : { - "model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - export interface ILinuxKeyboardLayoutInfo { - model: string; - layout: string; - variant: string; - options: string; - rules: string; - } - - /* __GDPR__FRAGMENT__ - "IKeyboardLayoutInfo" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - export interface IMacKeyboardLayoutInfo { - id: string; - lang: string; - } - - export type IKeyboardLayoutInfo = IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo; - - export function getCurrentKeyboardLayout(): IKeyboardLayoutInfo; - - export function onDidChangeKeyboardLayout(callback: () => void): void; - - export function isISOKeyboard(): boolean; -} \ No newline at end of file diff --git a/src/typings/native-watchdog.d.ts b/src/typings/native-watchdog.d.ts deleted file mode 100644 index 88119b314c128..0000000000000 --- a/src/typings/native-watchdog.d.ts +++ /dev/null @@ -1,17 +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 module 'native-watchdog' { - - /** - * Start monitoring for a certain pid to exist. - * If the process indicated by pid ceases to execute, - * the current process will exit in 6 seconds with exit code 87 - */ - export function start(pid: number): void; - - export function exit(exitCode: number): void; - -} diff --git a/src/typings/node-pty.d.ts b/src/typings/node-pty.d.ts deleted file mode 100644 index 2e6d6cfdc5f3d..0000000000000 --- a/src/typings/node-pty.d.ts +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) 2017, Daniel Imms (MIT License). - * Copyright (c) 2018, Microsoft Corporation (MIT License). - */ - -declare module 'node-pty' { - /** - * Forks a process as a pseudoterminal. - * @param file The file to launch. - * @param args The file's arguments as argv (string[]) or in a pre-escaped CommandLine format - * (string). Note that the CommandLine option is only available on Windows and is expected to be - * escaped properly. - * @param options The options of the terminal. - * @see CommandLineToArgvW https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx - * @see Parsing C++ Comamnd-Line Arguments https://msdn.microsoft.com/en-us/library/17w5ykft.aspx - * @see GetCommandLine https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156.aspx - */ - export function spawn(file: string, args: string[] | string, options: IPtyForkOptions | IWindowsPtyForkOptions): IPty; - - export interface IPtyForkOptions { - name?: string; - cols?: number; - rows?: number; - cwd?: string; - env?: { [key: string]: string }; - uid?: number; - gid?: number; - encoding?: string; - } - - export interface IWindowsPtyForkOptions { - name?: string; - cols?: number; - rows?: number; - cwd?: string; - env?: { [key: string]: string }; - encoding?: string; - /** - * Whether to use the experimental ConPTY system on Windows. When this is not set, ConPTY will - * be used when the Windows build number is >= 18309 (it's available in 17134 and 17692 but is - * too unstable to enable by default). - * - * This setting does nothing on non-Windows. - */ - experimentalUseConpty?: boolean; - /** - * Whether to use PSEUDOCONSOLE_INHERIT_CURSOR in conpty. - * @see https://docs.microsoft.com/en-us/windows/console/createpseudoconsole - */ - conptyInheritCursor?: boolean; - } - - /** - * An interface representing a pseudoterminal, on Windows this is emulated via the winpty library. - */ - export interface IPty { - /** - * The process ID of the outer process. - */ - readonly pid: number; - - /** - * The column size in characters. - */ - readonly cols: number; - - /** - * The row size in characters. - */ - readonly rows: number; - - /** - * The title of the active process. - */ - readonly process: string; - - /** - * Adds an event listener for when a data event fires. This happens when data is returned from - * the pty. - * @returns an `IDisposable` to stop listening. - */ - readonly onData: IEvent; - - /** - * Adds an event listener for when an exit event fires. This happens when the pty exits. - * @returns an `IDisposable` to stop listening. - */ - readonly onExit: IEvent<{ exitCode: number, signal?: number }>; - - /** - * Adds a listener to the data event, fired when data is returned from the pty. - * @param event The name of the event. - * @param listener The callback function. - * @deprecated Use IPty.onData - */ - on(event: 'data', listener: (data: string) => void): void; - - /** - * Adds a listener to the exit event, fired when the pty exits. - * @param event The name of the event. - * @param listener The callback function, exitCode is the exit code of the process and signal is - * the signal that triggered the exit. signal is not supported on Windows. - * @deprecated Use IPty.onExit - */ - on(event: 'exit', listener: (exitCode: number, signal?: number) => void): void; - - /** - * Resizes the dimensions of the pty. - * @param columns THe number of columns to use. - * @param rows The number of rows to use. - */ - resize(columns: number, rows: number): void; - - /** - * Writes data to the pty. - * @param data The data to write. - */ - write(data: string): void; - - /** - * Kills the pty. - * @param signal The signal to use, defaults to SIGHUP. This parameter is not supported on - * Windows. - * @throws Will throw when signal is used on Windows. - */ - kill(signal?: string): void; - } - - /** - * An object that can be disposed via a dispose function. - */ - export interface IDisposable { - dispose(): void; - } - - /** - * An event that can be listened to. - * @returns an `IDisposable` to stop listening. - */ - export interface IEvent { - (listener: (e: T) => any): IDisposable; - } -} diff --git a/src/typings/node.processEnv-ext.d.ts b/src/typings/node.processEnv-ext.d.ts deleted file mode 100644 index fec557ff2a74a..0000000000000 --- 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/nsfw.d.ts b/src/typings/nsfw.d.ts deleted file mode 100644 index f8bb423a2f822..0000000000000 --- a/src/typings/nsfw.d.ts +++ /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. - *--------------------------------------------------------------------------------------------*/ - -declare module 'nsfw' { - interface NsfwWatcher { - start(): any; - stop(): any; - } - - interface NsfwWatchingPromise { - then(): void; - } - - interface NsfwStartWatchingPromise { - then(fn: (watcher: NsfwWatcher) => void): NsfwWatchingPromise; - } - - interface NsfwEvent { - action: number; - directory: string; - file?: string; - newFile?: string; - newDirectory?: string; - oldFile?: string; - } - - interface NsfwFunction { - (dir: string, eventHandler: (events: NsfwEvent[]) => void, options?: any): NsfwStartWatchingPromise; - actions: { - CREATED: number; - DELETED: number; - MODIFIED: number; - RENAMED: number; - } - } - - var nsfw: NsfwFunction; - export = nsfw; -} diff --git a/src/typings/onigasm-umd.d.ts b/src/typings/onigasm-umd.d.ts deleted file mode 100644 index 151cecebfddb5..0000000000000 --- a/src/typings/onigasm-umd.d.ts +++ /dev/null @@ -1,33 +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 module "onigasm-umd" { - - function loadWASM(data: string | ArrayBuffer): Promise; - - class OnigString { - constructor(content: string); - readonly content: string; - readonly dispose?: () => void; - } - - class OnigScanner { - constructor(patterns: string[]); - findNextMatchSync(string: string | OnigString, startPosition: number): IOnigMatch; - } - - export interface IOnigCaptureIndex { - index: number - start: number - end: number - length: number - } - - export interface IOnigMatch { - index: number - captureIndices: IOnigCaptureIndex[] - scanner: OnigScanner - } -} \ No newline at end of file diff --git a/src/typings/require-monaco.d.ts b/src/typings/require-monaco.d.ts deleted file mode 100644 index 1d823ddb860bd..0000000000000 --- a/src/typings/require-monaco.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -interface NodeRequire { - toUrl(path: string): string; - (dependencies: string[], callback: (...args: any[]) => any, errorback?: (err: any) => void): any; - config(data: any): any; -} - -declare var require: NodeRequire; \ No newline at end of file diff --git a/src/typings/require.d.ts b/src/typings/require.d.ts index 618861a5bee19..cc4a4043ce166 100644 --- a/src/typings/require.d.ts +++ b/src/typings/require.d.ts @@ -31,7 +31,7 @@ declare class LoaderEvent { readonly detail: string; } -declare var define: { +declare const define: { (moduleName: string, dependencies: string[], callback: (...args: any[]) => any): any; (moduleName: string, dependencies: string[], definition: any): any; (moduleName: string, callback: (...args: any[]) => any): any; @@ -46,5 +46,8 @@ interface NodeRequire { config(data: any): any; onError: Function; __$__nodeRequire(moduleName: string): T; - getStats(): ReadonlyArray + getStats(): ReadonlyArray; + define(amdModuleId: string, dependencies: string[], callback: (...args: any[]) => any): any; } + +declare var require: NodeRequire; diff --git a/src/typings/semver-umd.d.ts b/src/typings/semver-umd.d.ts deleted file mode 100644 index 372f000c617ee..0000000000000 --- a/src/typings/semver-umd.d.ts +++ /dev/null @@ -1,10 +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 module 'semver-umd' { - - export * from "semver"; - -} \ No newline at end of file diff --git a/src/typings/spdlog.d.ts b/src/typings/spdlog.d.ts deleted file mode 100644 index bf6bb9c3dfdd4..0000000000000 --- a/src/typings/spdlog.d.ts +++ /dev/null @@ -1,40 +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 module 'spdlog' { - - export const version: string; - export function setAsyncMode(bufferSize: number, flushInterval: number): void; - export function createRotatingLogger(name: string, filename: string, filesize: number, filecount: number): RotatingLogger; - export function createRotatingLoggerAsync(name: string, filename: string, filesize: number, filecount: number): Promise; - - export enum LogLevel { - CRITICAL, - ERROR, - WARN, - INFO, - DEBUG, - TRACE, - OFF - } - - export class RotatingLogger { - constructor(name: string, filename: string, filesize: number, filecount: number); - - trace(message: string): void; - debug(message: string): void; - info(message: string): void; - warn(message: string): void; - error(message: string): void; - critical(message: string): void; - setLevel(level: number): void; - clearFormatters(): void; - /** - * A synchronous operation to flush the contents into file - */ - flush(): void; - drop(): void; - } -} \ No newline at end of file diff --git a/src/typings/sudo-prompt.d.ts b/src/typings/sudo-prompt.d.ts deleted file mode 100644 index 85f9783d0a669..0000000000000 --- a/src/typings/sudo-prompt.d.ts +++ /dev/null @@ -1,9 +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 module 'sudo-prompt' { - - export function exec(cmd: string, options: { name?: string, icns?: string }, callback: (error: string, stdout: string, stderr: string) => void): void; -} \ No newline at end of file diff --git a/src/typings/trustedTypes.d.ts b/src/typings/trustedTypes.d.ts new file mode 100644 index 0000000000000..feb4af291844e --- /dev/null +++ b/src/typings/trustedTypes.d.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// see https://w3c.github.io/webappsec-trusted-types/dist/spec/ +// this isn't complete nor 100% correct + +type TrustedHTML = string & object; +type TrustedScript = string; +type TrustedScriptURL = string; + +interface TrustedTypePolicyOptions { + createHTML?: (value: string) => string + createScript?: (value: string) => string + createScriptURL?: (value: string) => string +} + +interface TrustedTypePolicy { + readonly name: string; + createHTML(input: string, ...more: any[]): TrustedHTML + createScript(input: string, ...more: any[]): TrustedScript + createScriptURL(input: string, ...more: any[]): TrustedScriptURL +} + +interface TrustedTypePolicyFactory { + createPolicy(policyName: string, object: TrustedTypePolicyOptions): TrustedTypePolicy; +} + +interface Window { + trustedTypes: TrustedTypePolicyFactory | undefined; +} + +interface WorkerGlobalScope { + trustedTypes: TrustedTypePolicyFactory | undefined; +} diff --git a/src/typings/v8-inspect-profiler.d.ts b/src/typings/v8-inspect-profiler.d.ts deleted file mode 100644 index 59d7f81cfa065..0000000000000 --- a/src/typings/v8-inspect-profiler.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -declare module 'v8-inspect-profiler' { - - export interface ProfileResult { - profile: Profile; - } - - export interface Profile { - nodes: ProfileNode[]; - samples?: number[]; - timeDeltas?: number[]; - startTime: number; - endTime: number; - } - - export interface ProfileNode { - id: number; - hitCount?: number; - children?: number[]; - callFrame: { - url: string; - scriptId: string; - functionName: string; - lineNumber: number; - columnNumber: number; - }; - deoptReason?: string; - positionTicks?: { line: number; ticks: number }[]; - } - - export interface ProfilingSession { - stop(afterDelay?: number): PromiseLike; - } - - export interface Target { - description: string; - devtoolsFrontendUrl: string; - id: string; - title: string; - type: string; - url: string; - webSocketDebuggerUrl: string; - } - - export interface StartOptions { - port: number; - tries?: number; - retyWait?: number; - checkForPaused?: boolean; - target?: (targets: Target[]) => Target; - } - - export function startProfiling(options: StartOptions): PromiseLike; - export function writeProfile(profile: ProfileResult, name?: string): PromiseLike; - export function rewriteAbsolutePaths(profile: ProfileResult, replaceWith?: string): ProfileResult; -} diff --git a/src/typings/vscode-minimist.d.ts b/src/typings/vscode-minimist.d.ts deleted file mode 100644 index 17558a1a7383a..0000000000000 --- a/src/typings/vscode-minimist.d.ts +++ /dev/null @@ -1,92 +0,0 @@ -// Type definitions for minimist 1.2.0 -// Project: https://github.com/substack/minimist -// Definitions by: Bart van der Schoor , Necroskillz , kamranayub -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - -/** - * Return an argument object populated with the array arguments from args - * - * @param args An optional argument array (typically `process.argv.slice(2)`) - * @param opts An optional options object to customize the parsing - */ -declare function minimist(args?: string[], opts?: minimist.Opts): minimist.ParsedArgs; - -/** - * Return an argument object populated with the array arguments from args. Strongly-typed - * to be the intersect of type T with minimist.ParsedArgs. - * - * @type T The type that will be intersected with minimist.ParsedArgs to represent the argument object - * @param args An optional argument array (typically `process.argv.slice(2)`) - * @param opts An optional options object to customize the parsing - */ -declare function minimist(args?: string[], opts?: minimist.Opts): T & minimist.ParsedArgs; - -/** - * Return an argument object populated with the array arguments from args. Strongly-typed - * to be the the type T which should extend minimist.ParsedArgs - * - * @type T The type that extends minimist.ParsedArgs and represents the argument object - * @param args An optional argument array (typically `process.argv.slice(2)`) - * @param opts An optional options object to customize the parsing - */ -declare function minimist(args?: string[], opts?: minimist.Opts): T; - -declare namespace minimist { - export interface Opts { - /** - * A string or array of strings argument names to always treat as strings - */ - string?: string | string[]; - - /** - * A boolean, string or array of strings to always treat as booleans. If true will treat - * all double hyphenated arguments without equals signs as boolean (e.g. affects `--foo`, not `-f` or `--foo=bar`) - */ - boolean?: boolean | string | string[]; - - /** - * An object mapping string names to strings or arrays of string argument names to use as aliases - */ - alias?: { [key: string]: string | string[] }; - - /** - * An object mapping string argument names to default values - */ - default?: { [key: string]: any }; - - /** - * When true, populate argv._ with everything after the first non-option - */ - stopEarly?: boolean; - - /** - * A function which is invoked with a command line parameter not defined in the opts - * configuration object. If the function returns false, the unknown option is not added to argv - */ - unknown?: (arg: string) => boolean; - - /** - * When true, populate argv._ with everything before the -- and argv['--'] with everything after the --. - * Note that with -- set, parsing for arguments still stops after the `--`. - */ - '--'?: boolean; - } - - export interface ParsedArgs { - [arg: string]: any; - - /** - * If opts['--'] is true, populated with everything after the -- - */ - '--'?: string[]; - - /** - * Contains all the arguments that didn't have an option associated with them - */ - _: string[]; - } -} - -declare module "vscode-minimist" { - export = minimist; -} diff --git a/src/typings/vscode-proxy-agent.d.ts b/src/typings/vscode-proxy-agent.d.ts deleted file mode 100644 index a997fa97801c6..0000000000000 --- a/src/typings/vscode-proxy-agent.d.ts +++ /dev/null @@ -1,6 +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 module 'vscode-proxy-agent'; diff --git a/src/typings/vscode-ripgrep.d.ts b/src/typings/vscode-ripgrep.d.ts deleted file mode 100644 index 4c5c89c3ca89c..0000000000000 --- a/src/typings/vscode-ripgrep.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'vscode-ripgrep' { - export const rgPath: string; -} diff --git a/src/typings/vscode-sqlite3.d.ts b/src/typings/vscode-sqlite3.d.ts deleted file mode 100644 index 9a79c4ded1c4c..0000000000000 --- a/src/typings/vscode-sqlite3.d.ts +++ /dev/null @@ -1,115 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// Type definitions for sqlite3 3.1 -// Project: http://github.com/mapbox/node-sqlite3 -// Definitions by: Nick Malaguti -// Sumant Manne -// Behind The Math -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - -/// - -declare module 'vscode-sqlite3' { - import events = require("events"); - - export const OPEN_READONLY: number; - export const OPEN_READWRITE: number; - export const OPEN_CREATE: number; - export const OPEN_SHAREDCACHE: number; - export const OPEN_PRIVATECACHE: number; - export const OPEN_URI: number; - - export const cached: { - Database(filename: string, callback?: (this: Database, err: Error | null) => void): Database; - Database(filename: string, mode?: number, callback?: (this: Database, err: Error | null) => void): Database; - }; - - export interface RunResult extends Statement { - lastID: number; - changes: number; - } - - export class Statement extends events.EventEmitter { - bind(callback?: (err: Error | null) => void): this; - bind(...params: any[]): this; - - reset(callback?: (err: null) => void): this; - - finalize(callback?: (err: Error) => void): Database; - - run(callback?: (err: Error | null) => void): this; - run(params: any, callback?: (this: RunResult, err: Error | null) => void): this; - run(...params: any[]): this; - - get(callback?: (err: Error | null, row?: any) => void): this; - get(params: any, callback?: (this: RunResult, err: Error | null, row?: any) => void): this; - get(...params: any[]): this; - - all(callback?: (err: Error | null, rows: any[]) => void): this; - all(params: any, callback?: (this: RunResult, err: Error | null, rows: any[]) => void): this; - all(...params: any[]): this; - - each(callback?: (err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this; - each(params: any, callback?: (this: RunResult, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this; - each(...params: any[]): this; - } - - export class Database extends events.EventEmitter { - constructor(filename: string, callback?: (err: Error | null) => void); - constructor(filename: string, mode?: number, callback?: (err: Error | null) => void); - - close(callback?: (err: Error | null) => void): void; - - run(sql: string, callback?: (this: RunResult, err: Error | null) => void): this; - run(sql: string, params: any, callback?: (this: RunResult, err: Error | null) => void): this; - run(sql: string, ...params: any[]): this; - - get(sql: string, callback?: (this: Statement, err: Error | null, row: any) => void): this; - get(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: any) => void): this; - get(sql: string, ...params: any[]): this; - - all(sql: string, callback?: (this: Statement, err: Error | null, rows: any[]) => void): this; - all(sql: string, params: any, callback?: (this: Statement, err: Error | null, rows: any[]) => void): this; - all(sql: string, ...params: any[]): this; - - each(sql: string, callback?: (this: Statement, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this; - each(sql: string, params: any, callback?: (this: Statement, err: Error | null, row: any) => void, complete?: (err: Error | null, count: number) => void): this; - each(sql: string, ...params: any[]): this; - - exec(sql: string, callback?: (this: Statement, err: Error | null) => void): this; - - prepare(sql: string, callback?: (this: Statement, err: Error | null) => void): Statement; - prepare(sql: string, params: any, callback?: (this: Statement, err: Error | null) => void): Statement; - prepare(sql: string, ...params: any[]): Statement; - - serialize(callback?: () => void): void; - parallelize(callback?: () => void): void; - - on(event: "trace", listener: (sql: string) => void): this; - on(event: "profile", listener: (sql: string, time: number) => void): this; - on(event: "error", listener: (err: Error) => void): this; - on(event: "open" | "close", listener: () => void): this; - on(event: string, listener: (...args: any[]) => void): this; - - configure(option: "busyTimeout", value: number): void; - } - - export function verbose(): sqlite3; - - export interface sqlite3 { - OPEN_READONLY: number; - OPEN_READWRITE: number; - OPEN_CREATE: number; - OPEN_SHAREDCACHE: number; - OPEN_PRIVATECACHE: number; - OPEN_URI: number; - cached: typeof cached; - RunResult: RunResult; - Statement: typeof Statement; - Database: typeof Database; - verbose(): this; - } -} \ No newline at end of file diff --git a/src/typings/vscode-textmate.d.ts b/src/typings/vscode-textmate.d.ts deleted file mode 100644 index 835b33008ac50..0000000000000 --- a/src/typings/vscode-textmate.d.ts +++ /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. - *--------------------------------------------------------------------------------------------*/ - -declare module "vscode-textmate" { - /** - * A single theme setting. - */ - export interface IRawThemeSetting { - readonly name?: string; - readonly scope?: string | string[]; - readonly settings: { - readonly fontStyle?: string; - readonly foreground?: string; - readonly background?: string; - }; - } - /** - * A TextMate theme. - */ - export interface IRawTheme { - readonly name?: string; - readonly settings: IRawThemeSetting[]; - } - export interface Thenable extends PromiseLike { - } - /** - * A registry helper that can locate grammar file paths given scope names. - */ - export interface RegistryOptions { - theme?: IRawTheme; - loadGrammar(scopeName: string): Thenable; - getInjections?(scopeName: string): string[]; - getOnigLib?(): Thenable; - } - /** - * A map from scope name to a language id. Please do not use language id 0. - */ - export interface IEmbeddedLanguagesMap { - [scopeName: string]: number; - } - /** - * A map from selectors to token types. - */ - export interface ITokenTypeMap { - [selector: string]: StandardTokenType; - } - export const enum StandardTokenType { - Other = 0, - Comment = 1, - String = 2, - RegEx = 4, - } - export interface IGrammarConfiguration { - embeddedLanguages?: IEmbeddedLanguagesMap; - tokenTypes?: ITokenTypeMap; - } - /** - * The registry that will hold all grammars. - */ - export class Registry { - private readonly _locator; - private readonly _syncRegistry; - constructor(locator?: RegistryOptions); - /** - * Change the theme. Once called, no previous `ruleStack` should be used anymore. - */ - setTheme(theme: IRawTheme): void; - /** - * Returns a lookup array for color ids. - */ - getColorMap(): string[]; - /** - * Load the grammar for `scopeName` and all referenced included grammars asynchronously. - * Please do not use language id 0. - */ - loadGrammarWithEmbeddedLanguages(initialScopeName: string, initialLanguage: number, embeddedLanguages: IEmbeddedLanguagesMap): Thenable; - /** - * Load the grammar for `scopeName` and all referenced included grammars asynchronously. - * Please do not use language id 0. - */ - loadGrammarWithConfiguration(initialScopeName: string, initialLanguage: number, configuration: IGrammarConfiguration): Thenable; - /** - * Load the grammar for `scopeName` and all referenced included grammars asynchronously. - */ - loadGrammar(initialScopeName: string): Thenable; - private _loadGrammar; - /** - * Adds a rawGrammar. - */ - addGrammar(rawGrammar: IRawGrammar, injections?: string[], initialLanguage?: number, embeddedLanguages?: IEmbeddedLanguagesMap): Thenable; - /** - * Get the grammar for `scopeName`. The grammar must first be created via `loadGrammar` or `addGrammar`. - */ - grammarForScopeName(scopeName: string, initialLanguage?: number, embeddedLanguages?: IEmbeddedLanguagesMap, tokenTypes?: ITokenTypeMap): Thenable; - } - /** - * A grammar - */ - export interface IGrammar { - /** - * Tokenize `lineText` using previous line state `prevState`. - */ - tokenizeLine(lineText: string, prevState: StackElement | null): ITokenizeLineResult; - /** - * Tokenize `lineText` using previous line state `prevState`. - * The result contains the tokens in binary format, resolved with the following information: - * - language - * - token type (regex, string, comment, other) - * - font style - * - foreground color - * - background color - * e.g. for getting the languageId: `(metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET` - */ - tokenizeLine2(lineText: string, prevState: StackElement | null): ITokenizeLineResult2; - } - export interface ITokenizeLineResult { - readonly tokens: IToken[]; - /** - * The `prevState` to be passed on to the next line tokenization. - */ - readonly ruleStack: StackElement; - } - /** - * Helpers to manage the "collapsed" metadata of an entire StackElement stack. - * The following assumptions have been made: - * - languageId < 256 => needs 8 bits - * - unique color count < 512 => needs 9 bits - * - * The binary format is: - * - ------------------------------------------- - * 3322 2222 2222 1111 1111 1100 0000 0000 - * 1098 7654 3210 9876 5432 1098 7654 3210 - * - ------------------------------------------- - * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - * bbbb bbbb bfff ffff ffFF FTTT LLLL LLLL - * - ------------------------------------------- - * - L = LanguageId (8 bits) - * - T = StandardTokenType (3 bits) - * - F = FontStyle (3 bits) - * - f = foreground color (9 bits) - * - b = background color (9 bits) - */ - export const enum MetadataConsts { - LANGUAGEID_MASK = 255, - TOKEN_TYPE_MASK = 1792, - FONT_STYLE_MASK = 14336, - FOREGROUND_MASK = 8372224, - BACKGROUND_MASK = 4286578688, - LANGUAGEID_OFFSET = 0, - TOKEN_TYPE_OFFSET = 8, - FONT_STYLE_OFFSET = 11, - FOREGROUND_OFFSET = 14, - BACKGROUND_OFFSET = 23, - } - export interface ITokenizeLineResult2 { - /** - * The tokens in binary format. Each token occupies two array indices. For token i: - * - at offset 2*i => startIndex - * - at offset 2*i + 1 => metadata - * - */ - readonly tokens: Uint32Array; - /** - * The `prevState` to be passed on to the next line tokenization. - */ - readonly ruleStack: StackElement; - } - export interface IToken { - startIndex: number; - readonly endIndex: number; - readonly scopes: string[]; - } - /** - * **IMPORTANT** - Immutable! - */ - export interface StackElement { - _stackElementBrand: void; - readonly depth: number; - clone(): StackElement; - equals(other: StackElement): boolean; - } - export const INITIAL: StackElement; - export const parseRawGrammar: (content: string, filePath?: string) => IRawGrammar; - export interface ILocation { - readonly filename: string; - readonly line: number; - readonly char: number; - } - export interface ILocatable { - readonly $vscodeTextmateLocation?: ILocation; - } - export interface IRawGrammar extends ILocatable { - repository: IRawRepository; - readonly scopeName: string; - readonly patterns: IRawRule[]; - readonly injections?: { - [expression: string]: IRawRule; - }; - readonly injectionSelector?: string; - readonly fileTypes?: string[]; - readonly name?: string; - readonly firstLineMatch?: string; - } - export interface IRawRepositoryMap { - [name: string]: IRawRule; - $self: IRawRule; - $base: IRawRule; - } - export type IRawRepository = IRawRepositoryMap & ILocatable; - export interface IRawRule extends ILocatable { - id?: number; - readonly include?: string; - readonly name?: string; - readonly contentName?: string; - readonly match?: string; - readonly captures?: IRawCaptures; - readonly begin?: string; - readonly beginCaptures?: IRawCaptures; - readonly end?: string; - readonly endCaptures?: IRawCaptures; - readonly while?: string; - readonly whileCaptures?: IRawCaptures; - readonly patterns?: IRawRule[]; - readonly repository?: IRawRepository; - readonly applyEndPatternLast?: boolean; - } - export interface IRawCapturesMap { - [captureId: string]: IRawRule; - } - export type IRawCaptures = IRawCapturesMap & ILocatable; - export interface IOnigLib { - createOnigScanner(sources: string[]): OnigScanner; - createOnigString(sources: string): OnigString; - } - export interface IOnigCaptureIndex { - start: number; - end: number; - length: number; - } - export interface IOnigMatch { - index: number; - captureIndices: IOnigCaptureIndex[]; - scanner: OnigScanner; - } - export interface OnigScanner { - findNextMatchSync(string: string | OnigString, startPosition: number): IOnigMatch; - } - export interface OnigString { - readonly content: string; - readonly dispose?: () => void; - } - - -} diff --git a/src/typings/vscode-windows-ca-certs.d.ts b/src/typings/vscode-windows-ca-certs.d.ts deleted file mode 100644 index f923eb8a8aca5..0000000000000 --- a/src/typings/vscode-windows-ca-certs.d.ts +++ /dev/null @@ -1,6 +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 module 'vscode-windows-ca-certs'; diff --git a/src/typings/vscode-windows-registry.d.ts b/src/typings/vscode-windows-registry.d.ts deleted file mode 100644 index 9be14daa4b244..0000000000000 --- a/src/typings/vscode-windows-registry.d.ts +++ /dev/null @@ -1,9 +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 module 'vscode-windows-registry' { - export type HKEY = "HKEY_CURRENT_USER" | "HKEY_LOCAL_MACHINE" | "HKEY_CLASSES_ROOT" | "HKEY_USERS" | "HKEY_CURRENT_CONFIG"; - export function GetStringRegKey(hive: HKEY, path: string, name: string): string | undefined; -} \ No newline at end of file diff --git a/src/typings/vsda.d.ts b/src/typings/vsda.d.ts deleted file mode 100644 index 369e98483b61e..0000000000000 --- a/src/typings/vsda.d.ts +++ /dev/null @@ -1,10 +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 module 'vsda' { - export class signer { - sign(arg: any): any; - } -} diff --git a/src/typings/windows-foreground-love.d.ts b/src/typings/windows-foreground-love.d.ts deleted file mode 100644 index f7d20f13aef8e..0000000000000 --- a/src/typings/windows-foreground-love.d.ts +++ /dev/null @@ -1,10 +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 module 'windows-foreground-love' { - - export function allowSetForegroundWindow(pid?: number): boolean; - -} \ No newline at end of file diff --git a/src/typings/windows-mutex.d.ts b/src/typings/windows-mutex.d.ts deleted file mode 100644 index 07c5908def2f7..0000000000000 --- a/src/typings/windows-mutex.d.ts +++ /dev/null @@ -1,14 +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 module 'windows-mutex' { - export class Mutex { - constructor(name: string); - isActive(): boolean; - release(): void; - } - - export function isActive(name: string): boolean; -} diff --git a/src/typings/windows-process-tree.d.ts b/src/typings/windows-process-tree.d.ts deleted file mode 100644 index b9db4ea76a84e..0000000000000 --- a/src/typings/windows-process-tree.d.ts +++ /dev/null @@ -1,63 +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 module 'windows-process-tree' { - export enum ProcessDataFlag { - None = 0, - Memory = 1, - CommandLine = 2 - } - - export interface IProcessInfo { - pid: number; - ppid: number; - name: string; - - /** - * The working set size of the process, in bytes. - */ - memory?: number; - - /** - * The string returned is at most 512 chars, strings exceeding this length are truncated. - */ - commandLine?: string; - } - - export interface IProcessCpuInfo extends IProcessInfo { - cpu?: number; - } - - export interface IProcessTreeNode { - pid: number; - name: string; - memory?: number; - commandLine?: string; - children: IProcessTreeNode[]; - } - - /** - * Returns a tree of processes with the rootPid process as the root. - * @param rootPid - The pid of the process that will be the root of the tree. - * @param callback - The callback to use with the returned list of processes. - * @param flags - The flags for what process data should be included. - */ - export function getProcessTree(rootPid: number, callback: (tree: IProcessTreeNode) => void, flags?: ProcessDataFlag): void; - - /** - * Returns a list of processes containing the rootPid process and all of its descendants. - * @param rootPid - The pid of the process of interest. - * @param callback - The callback to use with the returned set of processes. - * @param flags - The flags for what process data should be included. - */ - export function getProcessList(rootPid: number, callback: (processList: IProcessInfo[]) => void, flags?: ProcessDataFlag): void; - - /** - * Returns the list of processes annotated with cpu usage information. - * @param processList - The list of processes. - * @param callback - The callback to use with the returned list of processes. - */ - export function getProcessCpuUsage(processList: IProcessInfo[], callback: (processListWithCpu: IProcessCpuInfo[]) => void): void; -} diff --git a/src/typings/xterm-addon-search.d.ts b/src/typings/xterm-addon-search.d.ts deleted file mode 100644 index 2f3be6f82bd24..0000000000000 --- a/src/typings/xterm-addon-search.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -// HACK: gulp-tsb doesn't play nice with importing from typings -// import { Terminal, ITerminalAddon } from 'xterm'; - -declare module 'xterm-addon-search' { - /** - * Options for a search. - */ - export interface ISearchOptions { - /** - * Whether the search term is a regex. - */ - regex?: boolean; - - /** - * Whether to search for a whole word, the result is only valid if it's - * suppounded in "non-word" characters such as `_`, `(`, `)` or space. - */ - wholeWord?: boolean; - - /** - * Whether the search is case sensitive. - */ - caseSensitive?: boolean; - - /** - * Whether to do an indcremental search, this will expand the selection if it - * still matches the term the user typed. Note that this only affects - * `findNext`, not `findPrevious`. - */ - incremental?: boolean; - } - - /** - * An xterm.js addon that provides search functionality. - */ - export class SearchAddon { - /** - * Activates the addon - * @param terminal The terminal the addon is being loaded in. - */ - public activate(terminal: any): void; - - /** - * Disposes the addon. - */ - public dispose(): void; - - /** - * Search forwards for the next result that matches the search term and - * options. - * @param term The search term. - * @param searchOptions The options for the search. - */ - public findNext(term: string, searchOptions?: ISearchOptions): boolean; - - /** - * Search backwards for the previous result that matches the search term and - * options. - * @param term The search term. - * @param searchOptions The options for the search. - */ - public findPrevious(term: string, searchOptions?: ISearchOptions): boolean; - } -} diff --git a/src/typings/xterm-addon-web-links.d.ts b/src/typings/xterm-addon-web-links.d.ts deleted file mode 100644 index da348f8c82eb5..0000000000000 --- a/src/typings/xterm-addon-web-links.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2017 The xterm.js authors. All rights reserved. - * @license MIT - */ - -// HACK: gulp-tsb doesn't play nice with importing from typings -// import { Terminal, ITerminalAddon } from 'xterm'; -interface ILinkMatcherOptions { - /** - * The index of the link from the regex.match(text) call. This defaults to 0 - * (for regular expressions without capture groups). - */ - matchIndex?: number; - - /** - * A callback that validates whether to create an individual link, pass - * whether the link is valid to the callback. - */ - validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void; - - /** - * A callback that fires when the mouse hovers over a link for a moment. - */ - tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void; - - /** - * A callback that fires when the mouse leaves a link. Note that this can - * happen even when tooltipCallback hasn't fired for the link yet. - */ - leaveCallback?: () => void; - - /** - * The priority of the link matcher, this defines the order in which the link - * matcher is evaluated relative to others, from highest to lowest. The - * default value is 0. - */ - priority?: number; - - /** - * A callback that fires when the mousedown and click events occur that - * determines whether a link will be activated upon click. This enables - * only activating a link when a certain modifier is held down, if not the - * mouse event will continue propagation (eg. double click to select word). - */ - willLinkActivate?: (event: MouseEvent, uri: string) => boolean; -} - -declare module 'xterm-addon-web-links' { - /** - * An xterm.js addon that enables web links. - */ - export class WebLinksAddon { - /** - * Creates a new web links addon. - * @param handler The callback when the link is called. - * @param options Options for the link matcher. - */ - constructor(handler?: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions); - - /** - * Activates the addon - * @param terminal The terminal the addon is being loaded in. - */ - public activate(terminal: any): void; - - /** - * Disposes the addon. - */ - public dispose(): void; - } -} diff --git a/src/typings/xterm.d.ts b/src/typings/xterm.d.ts deleted file mode 100644 index c82bd3c508655..0000000000000 --- a/src/typings/xterm.d.ts +++ /dev/null @@ -1,1105 +0,0 @@ -/** - * @license MIT - * - * This contains the type declarations for the xterm.js library. Note that - * some interfaces differ between this file and the actual implementation in - * src/, that's because this file declares the *public* API which is intended - * to be stable and consumed by external programs. - */ - -/// - -declare module 'xterm' { - /** - * A string representing text font weight. - */ - export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'; - - /** - * A string representing log level. - */ - export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off'; - - /** - * A string representing a renderer type. - */ - export type RendererType = 'dom' | 'canvas'; - - /** - * An object containing start up options for the terminal. - */ - export interface ITerminalOptions { - /** - * Whether background should support non-opaque color. It must be set before - * executing the `Terminal.open()` method and can't be changed later without - * executing it again. Note that enabling this can negatively impact - * performance. - */ - allowTransparency?: boolean; - - /** - * A data uri of the sound to use for the bell when `bellStyle = 'sound'`. - */ - bellSound?: string; - - /** - * The type of the bell notification the terminal will use. - */ - bellStyle?: 'none' /*| 'visual'*/ | 'sound' /*| 'both'*/; - - /** - * When enabled the cursor will be set to the beginning of the next line - * with every new line. This equivalent to sending '\r\n' for each '\n'. - * Normally the termios settings of the underlying PTY deals with the - * translation of '\n' to '\r\n' and this setting should not be used. If you - * deal with data from a non-PTY related source, this settings might be - * useful. - */ - convertEol?: boolean; - - /** - * The number of columns in the terminal. - */ - cols?: number; - - /** - * Whether the cursor blinks. - */ - cursorBlink?: boolean; - - /** - * The style of the cursor. - */ - cursorStyle?: 'block' | 'underline' | 'bar'; - - /** - * Whether input should be disabled. - */ - disableStdin?: boolean; - - /** - * Whether to draw bold text in bright colors. The default is true. - */ - drawBoldTextInBrightColors?: boolean; - - /** - * The modifier key hold to multiply scroll speed. - */ - fastScrollModifier?: 'alt' | 'ctrl' | 'shift' | undefined; - - /** - * The scroll speed multiplier used for fast scrolling. - */ - fastScrollSensitivity?: number; - - /** - * The font size used to render text. - */ - fontSize?: number; - - /** - * The font family used to render text. - */ - fontFamily?: string; - - /** - * The font weight used to render non-bold text. - */ - fontWeight?: FontWeight; - - /** - * The font weight used to render bold text. - */ - fontWeightBold?: FontWeight; - - /** - * The spacing in whole pixels between characters.. - */ - letterSpacing?: number; - - /** - * The line height used to render text. - */ - lineHeight?: number; - - /** - * What log level to use, this will log for all levels below and including - * what is set: - * - * 1. debug - * 2. info (default) - * 3. warn - * 4. error - * 5. off - */ - logLevel?: LogLevel; - - /** - * Whether to treat option as the meta key. - */ - macOptionIsMeta?: boolean; - - /** - * Whether holding a modifier key will force normal selection behavior, - * regardless of whether the terminal is in mouse events mode. This will - * also prevent mouse events from being emitted by the terminal. For - * example, this allows you to use xterm.js' regular selection inside tmux - * with mouse mode enabled. - */ - macOptionClickForcesSelection?: boolean; - - /** - * The type of renderer to use, this allows using the fallback DOM renderer - * when canvas is too slow for the environment. The following features do - * not work when the DOM renderer is used: - * - * - Letter spacing - * - Cursor blink - */ - rendererType?: RendererType; - - /** - * Whether to select the word under the cursor on right click, this is - * standard behavior in a lot of macOS applications. - */ - rightClickSelectsWord?: boolean; - - /** - * The number of rows in the terminal. - */ - rows?: number; - - /** - * Whether screen reader support is enabled. When on this will expose - * supporting elements in the DOM to support NVDA on Windows and VoiceOver - * on macOS. - */ - screenReaderMode?: boolean; - - /** - * The amount of scrollback in the terminal. Scrollback is the amount of - * rows that are retained when lines are scrolled beyond the initial - * viewport. - */ - scrollback?: number; - - /** - * The scrolling speed multiplier used for adjusting normal scrolling speed. - */ - scrollSensitivity?: number; - - /** - * The size of tab stops in the terminal. - */ - tabStopWidth?: number; - - /** - * The color theme of the terminal. - */ - theme?: ITheme; - - /** - * Whether "Windows mode" is enabled. Because Windows backends winpty and - * conpty operate by doing line wrapping on their side, xterm.js does not - * have access to wrapped lines. When Windows mode is enabled the following - * changes will be in effect: - * - * - Reflow is disabled. - * - Lines are assumed to be wrapped if the last character of the line is - * not whitespace. - */ - windowsMode?: boolean; - - /** - * A string containing all characters that are considered word separated by the - * double click to select work logic. - */ - wordSeparator?: string; - } - - /** - * Contains colors to theme the terminal with. - */ - export interface ITheme { - /** The default foreground color */ - foreground?: string; - /** The default background color */ - background?: string; - /** The cursor color */ - cursor?: string; - /** The accent color of the cursor (fg color for a block cursor) */ - cursorAccent?: string; - /** The selection background color (can be transparent) */ - selection?: string; - /** ANSI black (eg. `\x1b[30m`) */ - black?: string; - /** ANSI red (eg. `\x1b[31m`) */ - red?: string; - /** ANSI green (eg. `\x1b[32m`) */ - green?: string; - /** ANSI yellow (eg. `\x1b[33m`) */ - yellow?: string; - /** ANSI blue (eg. `\x1b[34m`) */ - blue?: string; - /** ANSI magenta (eg. `\x1b[35m`) */ - magenta?: string; - /** ANSI cyan (eg. `\x1b[36m`) */ - cyan?: string; - /** ANSI white (eg. `\x1b[37m`) */ - white?: string; - /** ANSI bright black (eg. `\x1b[1;30m`) */ - brightBlack?: string; - /** ANSI bright red (eg. `\x1b[1;31m`) */ - brightRed?: string; - /** ANSI bright green (eg. `\x1b[1;32m`) */ - brightGreen?: string; - /** ANSI bright yellow (eg. `\x1b[1;33m`) */ - brightYellow?: string; - /** ANSI bright blue (eg. `\x1b[1;34m`) */ - brightBlue?: string; - /** ANSI bright magenta (eg. `\x1b[1;35m`) */ - brightMagenta?: string; - /** ANSI bright cyan (eg. `\x1b[1;36m`) */ - brightCyan?: string; - /** ANSI bright white (eg. `\x1b[1;37m`) */ - brightWhite?: string; - } - - /** - * An object containing options for a link matcher. - */ - export interface ILinkMatcherOptions { - /** - * The index of the link from the regex.match(text) call. This defaults to 0 - * (for regular expressions without capture groups). - */ - matchIndex?: number; - - /** - * A callback that validates whether to create an individual link, pass - * whether the link is valid to the callback. - */ - validationCallback?: (uri: string, callback: (isValid: boolean) => void) => void; - - /** - * A callback that fires when the mouse hovers over a link for a moment. - */ - tooltipCallback?: (event: MouseEvent, uri: string, location: IViewportRange) => boolean | void; - - /** - * A callback that fires when the mouse leaves a link. Note that this can - * happen even when tooltipCallback hasn't fired for the link yet. - */ - leaveCallback?: () => void; - - /** - * The priority of the link matcher, this defines the order in which the - * link matcher is evaluated relative to others, from highest to lowest. The - * default value is 0. - */ - priority?: number; - - /** - * A callback that fires when the mousedown and click events occur that - * determines whether a link will be activated upon click. This enables - * only activating a link when a certain modifier is held down, if not the - * mouse event will continue propagation (eg. double click to select word). - */ - willLinkActivate?: (event: MouseEvent, uri: string) => boolean; - } - - /** - * An object that can be disposed via a dispose function. - */ - export interface IDisposable { - dispose(): void; - } - - /** - * An event that can be listened to. - * @returns an `IDisposable` to stop listening. - */ - export interface IEvent { - (listener: (e: T) => any): IDisposable; - } - - /** - * Represents a specific line in the terminal that is tracked when scrollback - * is trimmed and lines are added or removed. This is a single line that may - * be part of a larger wrapped line. - */ - export interface IMarker extends IDisposable { - /** - * A unique identifier for this marker. - */ - readonly id: number; - - /** - * Whether this marker is disposed. - */ - readonly isDisposed: boolean; - - /** - * The actual line index in the buffer at this point in time. This is set to - * -1 if the marker has been disposed. - */ - readonly line: number; - } - - /** - * The set of localizable strings. - */ - export interface ILocalizableStrings { - /** - * The aria label for the underlying input textarea for the terminal. - */ - promptLabel: string; - - /** - * Announcement for when line reading is suppressed due to too many lines - * being printed to the terminal when `screenReaderMode` is enabled. - */ - tooMuchOutput: string; - } - - /** - * The class that represents an xterm.js terminal. - */ - export class Terminal implements IDisposable { - /** - * The element containing the terminal. - */ - readonly element: HTMLElement | undefined; - - /** - * The textarea that accepts input for the terminal. - */ - readonly textarea: HTMLTextAreaElement | undefined; - - /** - * The number of rows in the terminal's viewport. Use - * `ITerminalOptions.rows` to set this in the constructor and - * `Terminal.resize` for when the terminal exists. - */ - readonly rows: number; - - /** - * The number of columns in the terminal's viewport. Use - * `ITerminalOptions.cols` to set this in the constructor and - * `Terminal.resize` for when the terminal exists. - */ - readonly cols: number; - - /** - * (EXPERIMENTAL) The terminal's current buffer, this might be either the - * normal buffer or the alt buffer depending on what's running in the - * terminal. - */ - readonly buffer: IBuffer; - - /** - * (EXPERIMENTAL) Get all markers registered against the buffer. If the alt - * buffer is active this will always return []. - */ - readonly markers: ReadonlyArray; - - /** - * (EXPERIMENTAL) Get the parser interface to register - * custom escape sequence handlers. - */ - readonly parser: IParser; - - /** - * Natural language strings that can be localized. - */ - static strings: ILocalizableStrings; - - /** - * Creates a new `Terminal` object. - * - * @param options An object containing a set of options. - */ - constructor(options?: ITerminalOptions); - - /** - * Adds an event listener for the cursor moves. - * @returns an `IDisposable` to stop listening. - */ - onCursorMove: IEvent; - - /** - * Adds an event listener for when a data event fires. This happens for - * example when the user types or pastes into the terminal. The event value - * is whatever `string` results, in a typical setup, this should be passed - * on to the backing pty. - * @returns an `IDisposable` to stop listening. - */ - onData: IEvent; - - /** - * Adds an event listener for a key is pressed. The event value contains the - * string that will be sent in the data event as well as the DOM event that - * triggered it. - * @returns an `IDisposable` to stop listening. - */ - onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>; - - /** - * Adds an event listener for when a line feed is added. - * @returns an `IDisposable` to stop listening. - */ - onLineFeed: IEvent; - - /** - * Adds an event listener for when a scroll occurs. The event value is the - * new position of the viewport. - * @returns an `IDisposable` to stop listening. - */ - onScroll: IEvent; - - /** - * Adds an event listener for when a selection change occurs. - * @returns an `IDisposable` to stop listening. - */ - onSelectionChange: IEvent; - - /** - * Adds an event listener for when rows are rendered. The event value - * contains the start row and end rows of the rendered area (ranges from `0` - * to `Terminal.rows - 1`). - * @returns an `IDisposable` to stop listening. - */ - onRender: IEvent<{ start: number, end: number }>; - - /** - * Adds an event listener for when the terminal is resized. The event value - * contains the new size. - * @returns an `IDisposable` to stop listening. - */ - onResize: IEvent<{ cols: number, rows: number }>; - - /** - * Adds an event listener for when an OSC 0 or OSC 2 title change occurs. - * The event value is the new title. - * @returns an `IDisposable` to stop listening. - */ - onTitleChange: IEvent; - - /** - * Unfocus the terminal. - */ - blur(): void; - - /** - * Focus the terminal. - */ - focus(): void; - - /** - * Resizes the terminal. It's best practice to debounce calls to resize, - * this will help ensure that the pty can respond to the resize event - * before another one occurs. - * @param x The number of columns to resize to. - * @param y The number of rows to resize to. - */ - resize(columns: number, rows: number): void; - - /** - * Opens the terminal within an element. - * @param parent The element to create the terminal within. This element - * must be visible (have dimensions) when `open` is called as several DOM- - * based measurements need to be performed when this function is called. - */ - open(parent: HTMLElement): void; - - /** - * Attaches a custom key event handler which is run before keys are - * processed, giving consumers of xterm.js ultimate control as to what keys - * should be processed by the terminal and what keys should not. - * @param customKeyEventHandler The custom KeyboardEvent handler to attach. - * This is a function that takes a KeyboardEvent, allowing consumers to stop - * propagation and/or prevent the default action. The function returns - * whether the event should be processed by xterm.js. - */ - attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void; - - /** - * (EXPERIMENTAL) Registers a link matcher, allowing custom link patterns to - * be matched and handled. - * @param regex The regular expression to search for, specifically this - * searches the textContent of the rows. You will want to use \s to match a - * space ' ' character for example. - * @param handler The callback when the link is called. - * @param options Options for the link matcher. - * @return The ID of the new matcher, this can be used to deregister. - */ - registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number; - - /** - * (EXPERIMENTAL) Deregisters a link matcher if it has been registered. - * @param matcherId The link matcher's ID (returned after register) - */ - deregisterLinkMatcher(matcherId: number): void; - - /** - * (EXPERIMENTAL) Registers a character joiner, allowing custom sequences of - * characters to be rendered as a single unit. This is useful in particular - * for rendering ligatures and graphemes, among other things. - * - * Each registered character joiner is called with a string of text - * representing a portion of a line in the terminal that can be rendered as - * a single unit. The joiner must return a sorted array, where each entry is - * itself an array of length two, containing the start (inclusive) and end - * (exclusive) index of a substring of the input that should be rendered as - * a single unit. When multiple joiners are provided, the results of each - * are collected. If there are any overlapping substrings between them, they - * are combined into one larger unit that is drawn together. - * - * All character joiners that are registered get called every time a line is - * rendered in the terminal, so it is essential for the handler function to - * run as quickly as possible to avoid slowdowns when rendering. Similarly, - * joiners should strive to return the smallest possible substrings to - * render together, since they aren't drawn as optimally as individual - * characters. - * - * NOTE: character joiners are only used by the canvas renderer. - * - * @param handler The function that determines character joins. It is called - * with a string of text that is eligible for joining and returns an array - * where each entry is an array containing the start (inclusive) and end - * (exclusive) indexes of ranges that should be rendered as a single unit. - * @return The ID of the new joiner, this can be used to deregister - */ - registerCharacterJoiner(handler: (text: string) => [number, number][]): number; - - /** - * (EXPERIMENTAL) Deregisters the character joiner if one was registered. - * NOTE: character joiners are only used by the canvas renderer. - * @param joinerId The character joiner's ID (returned after register) - */ - deregisterCharacterJoiner(joinerId: number): void; - - /** - * (EXPERIMENTAL) Adds a marker to the normal buffer and returns it. If the - * alt buffer is active, undefined is returned. - * @param cursorYOffset The y position offset of the marker from the cursor. - */ - addMarker(cursorYOffset: number): IMarker; - - /** - * Gets whether the terminal has an active selection. - */ - hasSelection(): boolean; - - /** - * Gets the terminal's current selection, this is useful for implementing - * copy behavior outside of xterm.js. - */ - getSelection(): string; - - /** - * Gets the selection position or undefined if there is no selection. - */ - getSelectionPosition(): ISelectionPosition | undefined; - - /** - * Clears the current terminal selection. - */ - clearSelection(): void; - - /** - * Selects text within the terminal. - * @param column The column the selection starts at.. - * @param row The row the selection starts at. - * @param length The length of the selection. - */ - select(column: number, row: number, length: number): void; - - /** - * Selects all text within the terminal. - */ - selectAll(): void; - - /** - * Selects text in the buffer between 2 lines. - * @param start The 0-based line index to select from (inclusive). - * @param end The 0-based line index to select to (inclusive). - */ - selectLines(start: number, end: number): void; - - /* - * Disposes of the terminal, detaching it from the DOM and removing any - * active listeners. - */ - dispose(): void; - - /** - * Scroll the display of the terminal - * @param amount The number of lines to scroll down (negative scroll up). - */ - scrollLines(amount: number): void; - - /** - * Scroll the display of the terminal by a number of pages. - * @param pageCount The number of pages to scroll (negative scrolls up). - */ - scrollPages(pageCount: number): void; - - /** - * Scrolls the display of the terminal to the top. - */ - scrollToTop(): void; - - /** - * Scrolls the display of the terminal to the bottom. - */ - scrollToBottom(): void; - - /** - * Scrolls to a line within the buffer. - * @param line The 0-based line index to scroll to. - */ - scrollToLine(line: number): void; - - /** - * Clear the entire buffer, making the prompt line the new first line. - */ - clear(): void; - - /** - * Write data to the terminal. - * @param data The data to write to the terminal. This can either be raw - * bytes given as Uint8Array from the pty or a string. Raw bytes will always - * be treated as UTF-8 encoded, string data as UTF-16. - * @param callback Optional callback that fires when the data was processed - * by the parser. - */ - write(data: string | Uint8Array, callback?: () => void): void; - - /** - * Writes data to the terminal, followed by a break line character (\n). - * @param data The data to write to the terminal. This can either be raw - * bytes given as Uint8Array from the pty or a string. Raw bytes will always - * be treated as UTF-8 encoded, string data as UTF-16. - * @param callback Optional callback that fires when the data was processed - * by the parser. - */ - writeln(data: string | Uint8Array, callback?: () => void): void; - - /** - * Write UTF8 data to the terminal. - * @param data The data to write to the terminal. - * @param callback Optional callback when data was processed. - * @deprecated use `write` instead - */ - writeUtf8(data: Uint8Array, callback?: () => void): void; - - /** - * Writes text to the terminal, performing the necessary transformations for pasted text. - * @param data The text to write to the terminal. - */ - paste(data: string): void; - - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string; - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean; - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: 'colors'): string[]; - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number; - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: 'handler'): (data: string) => void; - /** - * Retrieves an option's value from the terminal. - * @param key The option key. - */ - getOption(key: string): any; - - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'fontFamily' | 'termName' | 'bellSound' | 'wordSeparator', value: string): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'fontWeight' | 'fontWeightBold', value: null | 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'logLevel', value: LogLevel): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'bellStyle', value: null | 'none' | 'visual' | 'sound' | 'both'): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'cursorStyle', value: null | 'block' | 'underline' | 'bar'): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'colors', value: string[]): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'handler', value: (data: string) => void): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'theme', value: ITheme): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: 'cols' | 'rows', value: number): void; - /** - * Sets an option on the terminal. - * @param key The option key. - * @param value The option value. - */ - setOption(key: string, value: any): void; - - /** - * Tells the renderer to refresh terminal content between two rows - * (inclusive) at the next opportunity. - * @param start The row to start from (between 0 and this.rows - 1). - * @param end The row to end at (between start and this.rows - 1). - */ - refresh(start: number, end: number): void; - - /** - * Perform a full reset (RIS, aka '\x1bc'). - */ - reset(): void; - - /** - * Loads an addon into this instance of xterm.js. - * @param addon The addon to load. - */ - loadAddon(addon: ITerminalAddon): void; - } - - /** - * An addon that can provide additional functionality to the terminal. - */ - export interface ITerminalAddon extends IDisposable { - /** - * This is called when the addon is activated. - */ - activate(terminal: Terminal): void; - } - - /** - * An object representing a selection within the terminal. - */ - interface ISelectionPosition { - /** - * The start column of the selection. - */ - startColumn: number; - - /** - * The start row of the selection. - */ - startRow: number; - - /** - * The end column of the selection. - */ - endColumn: number; - - /** - * The end row of the selection. - */ - endRow: number; - } - - /** - * An object representing a range within the viewport of the terminal. - */ - export interface IViewportRange { - /** - * The start of the range. - */ - start: IViewportRangePosition; - - /** - * The end of the range. - */ - end: IViewportRangePosition; - } - - /** - * An object representing a cell position within the viewport of the terminal. - */ - interface IViewportRangePosition { - /** - * The x position of the cell. This is a 0-based index that refers to the - * space in between columns, not the column itself. Index 0 refers to the - * left side of the viewport, index `Terminal.cols` refers to the right side - * of the viewport. This can be thought of as how a cursor is positioned in - * a text editor. - */ - x: number; - - /** - * The y position of the cell. This is a 0-based index that refers to a - * specific row. - */ - y: number; - } - - /** - * Represents a terminal buffer. - */ - interface IBuffer { - /** - * The y position of the cursor. This ranges between `0` (when the - * cursor is at baseY) and `Terminal.rows - 1` (when the cursor is on the - * last row). - */ - readonly cursorY: number; - - /** - * The x position of the cursor. This ranges between `0` (left side) and - * `Terminal.cols - 1` (right side). - */ - readonly cursorX: number; - - /** - * The line within the buffer where the top of the viewport is. - */ - readonly viewportY: number; - - /** - * The line within the buffer where the top of the bottom page is (when - * fully scrolled down); - */ - readonly baseY: number; - - /** - * The amount of lines in the buffer. - */ - readonly length: number; - - /** - * Gets a line from the buffer, or undefined if the line index does not - * exist. - * - * Note that the result of this function should be used immediately after - * calling as when the terminal updates it could lead to unexpected - * behavior. - * - * @param y The line index to get. - */ - getLine(y: number): IBufferLine | undefined; - } - - /** - * Represents a line in the terminal's buffer. - */ - interface IBufferLine { - /** - * Whether the line is wrapped from the previous line. - */ - readonly isWrapped: boolean; - - /** - * Gets a cell from the line, or undefined if the line index does not exist. - * - * Note that the result of this function should be used immediately after - * calling as when the terminal updates it could lead to unexpected - * behavior. - * - * @param x The character index to get. - */ - getCell(x: number): IBufferCell | undefined; - - /** - * Gets the line as a string. Note that this is gets only the string for the - * line, not taking isWrapped into account. - * - * @param trimRight Whether to trim any whitespace at the right of the line. - * @param startColumn The column to start from (inclusive). - * @param endColumn The column to end at (exclusive). - */ - translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string; - } - - /** - * Represents a single cell in the terminal's buffer. - */ - interface IBufferCell { - /** - * The character within the cell. - */ - readonly char: string; - - /** - * The width of the character. Some examples: - * - * - This is `1` for most cells. - * - This is `2` for wide character like CJK glyphs. - * - This is `0` for cells immediately following cells with a width of `2`. - */ - readonly width: number; - } - - /** - * (EXPERIMENTAL) Data type to register a CSI, DCS or ESC callback in the parser - * in the form: - * ESC I..I F - * CSI Prefix P..P I..I F - * DCS Prefix P..P I..I F data_bytes ST - * - * with these rules/restrictions: - * - prefix can only be used with CSI and DCS - * - only one leading prefix byte is recognized by the parser - * before any other parameter bytes (P..P) - * - intermediate bytes are recognized up to 2 - * - * For custom sequences make sure to read ECMA-48 and the resources at - * vt100.net to not clash with existing sequences or reserved address space. - * General recommendations: - * - use private address space (see ECMA-48) - * - use max one intermediate byte (technically not limited by the spec, - * in practice there are no sequences with more than one intermediate byte, - * thus parsers might get confused with more intermediates) - * - test against other common emulators to check whether they escape/ignore - * the sequence correctly - * - * Notes: OSC command registration is handled differently (see addOscHandler) - * APC, PM or SOS is currently not supported. - */ - export interface IFunctionIdentifier { - /** - * Optional prefix byte, must be in range \x3c .. \x3f. - * Usable in CSI and DCS. - */ - prefix?: string; - /** - * Optional intermediate bytes, must be in range \x20 .. \x2f. - * Usable in CSI, DCS and ESC. - */ - intermediates?: string; - /** - * Final byte, must be in range \x40 .. \x7e for CSI and DCS, - * \x30 .. \x7e for ESC. - */ - final: string; - } - - /** - * (EXPERIMENTAL) Parser interface. - */ - export interface IParser { - /** - * Adds a handler for CSI escape sequences. - * @param id Specifies the function identifier under which the callback - * gets registered, e.g. {final: 'm'} for SGR. - * @param callback The function to handle the sequence. The callback is - * called with the numerical params. If the sequence has subparams the - * array will contain subarrays with their numercial values. - * Return true if the sequence was handled; false if we should try - * a previous handler (set by addCsiHandler or setCsiHandler). - * The most recently-added handler is tried first. - * @return An IDisposable you can call to remove this handler. - */ - addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable; - - /** - * Adds a handler for DCS escape sequences. - * @param id Specifies the function identifier under which the callback - * gets registered, e.g. {intermediates: '$' final: 'q'} for DECRQSS. - * @param callback The function to handle the sequence. Note that the - * function will only be called once if the sequence finished sucessfully. - * There is currently no way to intercept smaller data chunks, data chunks - * will be stored up until the sequence is finished. Since DCS sequences - * are not limited by the amount of data this might impose a problem for - * big payloads. Currently xterm.js limits DCS payload to 10 MB - * which should give enough room for most use cases. - * The function gets the payload and numerical parameters as arguments. - * Return true if the sequence was handled; false if we should try - * a previous handler (set by addDcsHandler or setDcsHandler). - * The most recently-added handler is tried first. - * @return An IDisposable you can call to remove this handler. - */ - addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable; - - /** - * Adds a handler for ESC escape sequences. - * @param id Specifies the function identifier under which the callback - * gets registered, e.g. {intermediates: '%' final: 'G'} for - * default charset selection. - * @param callback The function to handle the sequence. - * Return true if the sequence was handled; false if we should try - * a previous handler (set by addEscHandler or setEscHandler). - * The most recently-added handler is tried first. - * @return An IDisposable you can call to remove this handler. - */ - addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable; - - /** - * Adds a handler for OSC escape sequences. - * @param ident The number (first parameter) of the sequence. - * @param callback The function to handle the sequence. Note that the - * function will only be called once if the sequence finished sucessfully. - * There is currently no way to intercept smaller data chunks, data chunks - * will be stored up until the sequence is finished. Since OSC sequences - * are not limited by the amount of data this might impose a problem for - * big payloads. Currently xterm.js limits OSC payload to 10 MB - * which should give enough room for most use cases. - * The callback is called with OSC data string. - * Return true if the sequence was handled; false if we should try - * a previous handler (set by addOscHandler or setOscHandler). - * The most recently-added handler is tried first. - * @return An IDisposable you can call to remove this handler. - */ - addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable; - } -} diff --git a/src/typings/yauzl.d.ts b/src/typings/yauzl.d.ts deleted file mode 100644 index 39ef2ad9488e2..0000000000000 --- a/src/typings/yauzl.d.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. - *--------------------------------------------------------------------------------------------*/ - -declare module 'yauzl' { - - import { EventEmitter } from 'events'; - import { Readable } from 'stream'; - - export class Entry { - fileName: string; - extraFields: { id: number; data: Buffer; }[]; - comment: string; - versionMadeBy: number; - versionNeededToExtract: number; - generalPurposeBitFlag: number; - compressionMethod: number; - lastModFileTime: number; - lastModFileDate: number; - crc32: number; - compressedSize: number; - uncompressedSize: number; - fileNameLength: number; - extraFieldLength: number; - fileCommentLength: number; - internalFileAttributes: number; - externalFileAttributes: number; - relativeOffsetOfLocalHeader: number; - getLastModDate(): Date; - } - - export class ZipFile extends EventEmitter { - readEntry(): void; - openReadStream(entry: Entry, callback: (err?: Error, stream?: Readable) => void): void; - close(): void; - isOpen: boolean; - entryCount: number; - comment: string; - } - - export interface IOptions { - autoClose?: boolean; - lazyEntries?: boolean; - } - - export function open(path: string, callback: (err?: Error, zipfile?: ZipFile) => void): void; - export function open(path: string, options: IOptions | undefined, callback: (err?: Error, zipfile?: ZipFile) => void): void; -} \ No newline at end of file diff --git a/src/typings/yazl.d.ts b/src/typings/yazl.d.ts deleted file mode 100644 index 5644d092913ef..0000000000000 --- a/src/typings/yazl.d.ts +++ /dev/null @@ -1,15 +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 module 'yazl' { - import * as stream from 'stream'; - - class ZipFile { - outputStream: stream.Stream; - addBuffer(buffer: Buffer, path: string): void; - addFile(localPath: string, path: string): void; - end(): void; - } -} \ No newline at end of file diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index af7a3b653fc8f..5f811db8d6bf8 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -110,34 +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 isTouchDevice = 'ontouchstart' in window as any || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0; -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); - -export function hasClipboardSupport() { - if (isIE) { - return false; - } - - if (isEdge) { - let index = userAgent.indexOf('Edge/'); - let version = parseInt(userAgent.substring(index + 5, userAgent.indexOf('.', index)), 10); - - if (!version || (version >= 12 && version <= 16)) { - return false; - } - } - - return true; -} diff --git a/src/vs/base/browser/canIUse.ts b/src/vs/base/browser/canIUse.ts new file mode 100644 index 0000000000000..3f5391410a733 --- /dev/null +++ b/src/vs/base/browser/canIUse.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. + *--------------------------------------------------------------------------------------------*/ + +import * as browser from 'vs/base/browser/browser'; +import * as platform from 'vs/base/common/platform'; + +export const enum KeyboardSupport { + Always, + FullScreen, + None +} + +/** + * Browser feature we can support in current platform, browser and environment. + */ +export const BrowserFeatures = { + clipboard: { + writeText: ( + platform.isNative + || (document.queryCommandSupported && document.queryCommandSupported('copy')) + || !!(navigator && navigator.clipboard && navigator.clipboard.writeText) + ), + readText: ( + platform.isNative + || !!(navigator && navigator.clipboard && navigator.clipboard.readText) + ), + richText: (() => { + if (browser.isEdge) { + let index = navigator.userAgent.indexOf('Edge/'); + let version = parseInt(navigator.userAgent.substring(index + 5, navigator.userAgent.indexOf('.', index)), 10); + + if (!version || (version >= 12 && version <= 16)) { + return false; + } + } + + return true; + })() + }, + keyboard: (() => { + if (platform.isNative || browser.isStandalone) { + return KeyboardSupport.Always; + } + + if ((navigator).keyboard || browser.isSafari) { + return KeyboardSupport.FullScreen; + } + + return KeyboardSupport.None; + })(), + + // '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 0000000000000..ab29f13ce8b00 --- /dev/null +++ b/src/vs/base/browser/codicons.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 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})` : dom.$(`span.codicon.codicon-${name}${animation ? `.codicon-animation-${animation}` : ''}`)); + } + + if (textStart < text.length) { + elements.push(text.substring(textStart)); + } + return elements; +} diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index caf1d299038f7..2119700f85914 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 db901ed9e5e09..77aad1e356bd3 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -8,14 +8,13 @@ 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 * 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 { BrowserFeatures } from 'vs/base/browser/canIUse'; export function clearNode(node: HTMLElement): void { while (node.firstChild) { @@ -23,6 +22,9 @@ export function clearNode(node: HTMLElement): void { } } +/** + * @deprecated use `node.remove()` instead + */ export function removeNode(node: HTMLElement): void { if (node.parentNode) { node.parentNode.removeChild(node); @@ -48,117 +50,7 @@ interface IDomClassList { 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 { +const _classList: IDomClassList = new class implements IDomClassList { hasClass(node: HTMLElement, className: string): boolean { return Boolean(className) && node.classList && node.classList.contains(className); } @@ -190,15 +82,18 @@ const _nativeClassList = new class implements IDomClassList { } }; -// 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); +/** @deprecated ES6 - use classList*/ +export function hasClass(node: HTMLElement | SVGElement, className: string): boolean { return _classList.hasClass(node, className); } +/** @deprecated ES6 - use classList*/ +export function addClass(node: HTMLElement | SVGElement, className: string): void { return _classList.addClass(node, className); } +/** @deprecated ES6 - use classList*/ +export function addClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void { return _classList.addClasses(node, ...classNames); } +/** @deprecated ES6 - use classList*/ +export function removeClass(node: HTMLElement | SVGElement, className: string): void { return _classList.removeClass(node, className); } +/** @deprecated ES6 - use classList*/ +export function removeClasses(node: HTMLElement | SVGElement, ...classNames: string[]): void { return _classList.removeClasses(node, ...classNames); } +/** @deprecated ES6 - use classList*/ +export function toggleClass(node: HTMLElement | SVGElement, className: string, shouldHaveIt?: boolean): void { return _classList.toggleClass(node, className, shouldHaveIt); } class DomListener implements IDisposable { @@ -231,9 +126,9 @@ class DomListener implements IDisposable { export function addDisposableListener(node: EventTarget, type: K, handler: (event: GlobalEventHandlersEventMap[K]) => void, useCapture?: boolean): IDisposable; export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable; -export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture: AddEventListenerOptions): IDisposable; -export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture?: boolean | AddEventListenerOptions): IDisposable { - return new DomListener(node, type, handler, useCapture); +export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, options: AddEventListenerOptions): IDisposable; +export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCaptureOrOptions?: boolean | AddEventListenerOptions): IDisposable { + return new DomListener(node, type, handler, useCaptureOrOptions); } export interface IAddStandardDisposableListenerSignature { @@ -266,10 +161,47 @@ export let addStandardDisposableListener: IAddStandardDisposableListenerSignatur return addDisposableListener(node, type, wrapHandler, useCapture); }; +export let addStandardDisposableGenericMouseDownListner = function addStandardDisposableListener(node: HTMLElement, handler: (event: any) => void, useCapture?: boolean): IDisposable { + let wrapHandler = _wrapAsStandardMouseEvent(handler); + + 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); +} + +export function addDisposableGenericMouseMoveListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable { + return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_MOVE : EventType.MOUSE_MOVE, handler, useCapture); +} + +export function addDisposableGenericMouseUpListner(node: EventTarget, handler: (event: any) => void, useCapture?: boolean): IDisposable { + return addDisposableListener(node, platform.isIOS && BrowserFeatures.pointerEvents ? EventType.POINTER_UP : EventType.MOUSE_UP, handler, useCapture); +} export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { return addDisposableListener(node, 'mouseout', (e: MouseEvent) => { // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = (e.relatedTarget || e.target); + let toElement: Node | null = (e.relatedTarget); + while (toElement && toElement !== node) { + toElement = toElement.parentNode; + } + if (toElement === node) { + return; + } + + handler(e); + }); +} + +export function addDisposableNonBubblingPointerOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { + return addDisposableListener(node, 'pointerout', (e: MouseEvent) => { + // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements + let toElement: Node | null = (e.relatedTarget); while (toElement && toElement !== node) { toElement = toElement.parentNode; } @@ -428,7 +360,7 @@ export interface DOMEvent { } const MINIMUM_TIME_MS = 16; -const DEFAULT_EVENT_MERGER: IEventMerger = function (lastEvent: DOMEvent, currentEvent: DOMEvent) { +const DEFAULT_EVENT_MERGER: IEventMerger = function (lastEvent: DOMEvent | null, currentEvent: DOMEvent) { return currentEvent; }; @@ -477,6 +409,20 @@ export function getClientArea(element: HTMLElement): Dimension { return new Dimension(element.clientWidth, element.clientHeight); } + // 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 - ( + 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 + // plus the area between onscreen keyboard and the bottom bezel, which is 20px on iOS. + ? (20 + 4) // + 4px for body margin + : 0 + ); + return new Dimension(width, height); + } + // Try innerWidth / innerHeight if (window.innerWidth && window.innerHeight) { return new Dimension(window.innerWidth, window.innerHeight); @@ -559,7 +505,12 @@ 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, @@ -581,11 +532,17 @@ export function getTopLeftOffset(element: HTMLElement): { left: number; top: num // Adapted from WinJS.Utilities.getPosition // and added borders to the mix - let offsetParent = element.offsetParent, top = element.offsetTop, left = element.offsetLeft; + let offsetParent = element.offsetParent; + let top = element.offsetTop; + let left = element.offsetLeft; - while ((element = element.parentNode) !== null && element !== document.body && element !== document.documentElement) { + while ( + (element = element.parentNode) !== null + && element !== document.body + && element !== document.documentElement + ) { top -= element.scrollTop; - let c = getComputedStyle(element); + const c = isShadowRoot(element) ? null : getComputedStyle(element); if (c) { left -= c.direction !== 'rtl' ? element.scrollLeft : -element.scrollLeft; } @@ -746,7 +703,7 @@ export function isAncestor(testChild: Node | null, testAncestor: Node | null): b } export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): HTMLElement | null { - while (node) { + while (node && node.nodeType === node.ELEMENT_NODE) { if (hasClass(node, clazz)) { return node; } @@ -773,6 +730,37 @@ export function hasParentWithClass(node: HTMLElement, clazz: string, stopAtClazz return !!findParentWithClass(node, clazz, stopAtClazzOrNode); } +export function isShadowRoot(node: Node): node is ShadowRoot { + return ( + node && !!(node).host && !!(node).mode + ); +} + +export function isInShadowDOM(domNode: Node): boolean { + return !!getShadowRoot(domNode); +} + +export function getShadowRoot(domNode: Node): ShadowRoot | null { + while (domNode.parentNode) { + if (domNode === document.body) { + // reached the body + return null; + } + domNode = domNode.parentNode; + } + 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'; @@ -796,11 +784,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; } @@ -844,6 +832,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', @@ -852,6 +841,10 @@ 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', CONTEXT_MENU: 'contextmenu', WHEEL: 'wheel', // Keyboard @@ -922,6 +915,7 @@ export const EventHelper = { export interface IFocusTracker extends Disposable { onDidFocus: Event; onDidBlur: Event; + refreshState?(): void; } export function saveParentsScrollTop(node: Element): number[] { @@ -950,6 +944,8 @@ class FocusTracker extends Disposable implements IFocusTracker { private readonly _onDidBlur = this._register(new Emitter()); public readonly onDidBlur: Event = this._onDidBlur.event; + private _refreshStateHandler: () => void; + constructor(element: HTMLElement | Window) { super(); let hasFocus = isAncestor(document.activeElement, element); @@ -976,15 +972,35 @@ class FocusTracker extends Disposable implements IFocusTracker { } }; + this._refreshStateHandler = () => { + let currentNodeHasFocus = isAncestor(document.activeElement, element); + if (currentNodeHasFocus !== hasFocus) { + if (hasFocus) { + onBlur(); + } else { + onFocus(); + } + } + }; + this._register(domEvent(element, EventType.FOCUS, true)(onFocus)); this._register(domEvent(element, EventType.BLUR, true)(onBlur)); } + + refreshState() { + this._refreshStateHandler(); + } } 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]; @@ -995,7 +1011,22 @@ 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) { + parent.innerText = ''; + 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', @@ -1029,6 +1060,11 @@ function _$(namespace: Namespace, description: string, attrs? Object.keys(attrs).forEach(name => { const value = attrs![name]; + + if (typeof value === 'undefined') { + return; + } + if (/^on\w+$/.test(name)) { (result)[name] = value; } else if (name === 'selected') { @@ -1041,14 +1077,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; } @@ -1094,7 +1129,7 @@ export function hide(...elements: HTMLElement[]): void { } function findParentWithAttribute(node: Node | null, attribute: string): HTMLElement | null { - while (node) { + while (node && node.nodeType === node.ELEMENT_NODE) { if (node instanceof HTMLElement && node.hasAttribute(attribute)) { return node; } @@ -1213,7 +1248,22 @@ export function asCSSUrl(uri: URI): string { return `url('${asDomUri(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. @@ -1221,7 +1271,7 @@ 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 diff --git a/src/vs/base/browser/fastDomNode.ts b/src/vs/base/browser/fastDomNode.ts index 6dfec19f492bf..a5f9c18b2d6a5 100644 --- a/src/vs/base/browser/fastDomNode.ts +++ b/src/vs/base/browser/fastDomNode.ts @@ -25,7 +25,10 @@ export class FastDomNode { private _display: string; private _position: string; private _visibility: string; + private _backgroundColor: string; private _layerHint: boolean; + private _contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint'; + private _boxShadow: string; constructor(domNode: T) { this.domNode = domNode; @@ -46,7 +49,10 @@ export class FastDomNode { this._display = ''; this._position = ''; this._visibility = ''; + this._backgroundColor = ''; this._layerHint = false; + this._contain = 'none'; + this._boxShadow = ''; } public setMaxWidth(maxWidth: number): void { @@ -198,12 +204,36 @@ export class FastDomNode { this.domNode.style.visibility = this._visibility; } + public setBackgroundColor(backgroundColor: string): void { + if (this._backgroundColor === backgroundColor) { + return; + } + this._backgroundColor = backgroundColor; + this.domNode.style.backgroundColor = this._backgroundColor; + } + public setLayerHinting(layerHint: boolean): void { if (this._layerHint === layerHint) { return; } this._layerHint = layerHint; - (this.domNode.style).willChange = this._layerHint ? 'transform' : 'auto'; + 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; + } + this._contain = contain; + (this.domNode.style).contain = this._contain; } public setAttribute(name: string, value: string): void { @@ -214,11 +244,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 109f6f722604e..328ecaee03d8c 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -4,18 +4,21 @@ *--------------------------------------------------------------------------------------------*/ 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; + buttons: number; posx: number; posy: number; } export interface IEventMerger { - (lastEvent: R, currentEvent: MouseEvent): R; + (lastEvent: R | null, currentEvent: MouseEvent): R; } export interface IMouseMoveCallback { @@ -26,26 +29,27 @@ export interface IOnStopCallback { (): void; } -export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData, currentEvent: MouseEvent): IStandardMouseMoveEventData { +export function standardMouseMoveMerger(lastEvent: IStandardMouseMoveEventData | null, currentEvent: MouseEvent): IStandardMouseMoveEventData { let ev = new StandardMouseEvent(currentEvent); ev.preventDefault(); return { leftButton: ev.leftButton, + buttons: ev.buttons, posx: ev.posx, posy: ev.posy }; } -export class GlobalMouseMoveMonitor implements IDisposable { +export class GlobalMouseMoveMonitor implements IDisposable { - private readonly hooks = new DisposableStore(); - private mouseMoveEventMerger: IEventMerger | null = null; - private mouseMoveCallback: IMouseMoveCallback | null = null; - private onStopCallback: IOnStopCallback | null = null; + private readonly _hooks = new DisposableStore(); + private _mouseMoveEventMerger: IEventMerger | null = null; + private _mouseMoveCallback: IMouseMoveCallback | null = null; + private _onStopCallback: IOnStopCallback | null = null; public dispose(): void { this.stopMonitoring(false); - this.hooks.dispose(); + this._hooks.dispose(); } public stopMonitoring(invokeStopCallback: boolean): void { @@ -55,11 +59,11 @@ export class GlobalMouseMoveMonitor implements IDisposable { } // Unhook - this.hooks.clear(); - this.mouseMoveEventMerger = null; - this.mouseMoveCallback = null; - const onStopCallback = this.onStopCallback; - this.onStopCallback = null; + this._hooks.clear(); + this._mouseMoveEventMerger = null; + this._mouseMoveCallback = null; + const onStopCallback = this._onStopCallback; + this._onStopCallback = null; if (invokeStopCallback && onStopCallback) { onStopCallback(); @@ -67,10 +71,12 @@ export class GlobalMouseMoveMonitor implements IDisposable { } public isMonitoring(): boolean { - return !!this.mouseMoveEventMerger; + return !!this._mouseMoveEventMerger; } public startMonitoring( + initialElement: HTMLElement, + initialButtons: number, mouseMoveEventMerger: IEventMerger, mouseMoveCallback: IMouseMoveCallback, onStopCallback: IOnStopCallback @@ -79,38 +85,54 @@ export class GlobalMouseMoveMonitor implements IDisposable { // I am already hooked return; } - this.mouseMoveEventMerger = mouseMoveEventMerger; - this.mouseMoveCallback = mouseMoveCallback; - this.onStopCallback = onStopCallback; - - let windowChain = IframeUtils.getSameOriginWindowChain(); - for (const element of windowChain) { - this.hooks.add(dom.addDisposableThrottledListener(element.window.document, 'mousemove', - (data: R) => this.mouseMoveCallback!(data), - (lastEvent: R, currentEvent) => this.mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) + this._mouseMoveEventMerger = mouseMoveEventMerger; + this._mouseMoveCallback = mouseMoveCallback; + this._onStopCallback = onStopCallback; + + const windowChain = IframeUtils.getSameOriginWindowChain(); + const mouseMove = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove'; + const mouseUp = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup'; + + const listenTo: (Document | ShadowRoot)[] = windowChain.map(element => element.window.document); + const shadowRoot = dom.getShadowRoot(initialElement); + if (shadowRoot) { + listenTo.unshift(shadowRoot); + } + + for (const element of listenTo) { + this._hooks.add(dom.addDisposableThrottledListener(element, mouseMove, + (data: R) => { + if (data.buttons !== initialButtons) { + // Buttons state has changed in the meantime + this.stopMonitoring(true); + return; + } + this._mouseMoveCallback!(data); + }, + (lastEvent: R | null, currentEvent) => this._mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) )); - this.hooks.add(dom.addDisposableListener(element.window.document, 'mouseup', (e: MouseEvent) => this.stopMonitoring(true))); + this._hooks.add(dom.addDisposableListener(element, mouseUp, (e: MouseEvent) => this.stopMonitoring(true))); } if (IframeUtils.hasDifferentOriginAncestor()) { let lastSameOriginAncestor = windowChain[windowChain.length - 1]; // We might miss a mouse up if it happens outside the iframe // This one is for Chrome - this.hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseout', (browserEvent: MouseEvent) => { + this._hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseout', (browserEvent: MouseEvent) => { let e = new StandardMouseEvent(browserEvent); if (e.target.tagName.toLowerCase() === 'html') { this.stopMonitoring(true); } })); // This one is for FF - this.hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseover', (browserEvent: MouseEvent) => { + this._hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document, 'mouseover', (browserEvent: MouseEvent) => { let e = new StandardMouseEvent(browserEvent); if (e.target.tagName.toLowerCase() === 'html') { this.stopMonitoring(true); } })); // This one is for IE - this.hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document.body, 'mouseleave', (browserEvent: MouseEvent) => { + this._hooks.add(dom.addDisposableListener(lastSameOriginAncestor.window.document.body, 'mouseleave', (browserEvent: MouseEvent) => { this.stopMonitoring(true); })); } diff --git a/src/vs/base/browser/iframe.ts b/src/vs/base/browser/iframe.ts index 7868cafbba236..ade89e96ccccb 100644 --- a/src/vs/base/browser/iframe.ts +++ b/src/vs/base/browser/iframe.ts @@ -98,7 +98,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 { diff --git a/src/vs/base/browser/keyboardEvent.ts b/src/vs/base/browser/keyboardEvent.ts index 03bdffc95ede0..83e1ba0ad597a 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 57144d67ea618..7ccb91a83cd18 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -15,16 +15,25 @@ 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 { 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'; + +export interface MarkedOptions extends marked.MarkedOptions { + baseUrl?: never; +} export interface MarkdownRenderOptions extends FormattedTextRenderOptions { codeBlockRenderer?: (modeId: string, value: string) => Promise; codeBlockRenderCallback?: () => void; + baseUrl?: URI; } /** * Create html nodes for the given content element. */ -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 { @@ -50,19 +59,23 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende const _href = function (href: string, isDomUri: boolean): string { const data = markdown.uris && markdown.uris[href]; if (!data) { - return href; + return href; // no uri exists } let uri = URI.revive(data); + if (URI.parse(href).toString() === uri.toString()) { + 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 DOM.asDomUri(uri).toString(true); } if (uri.query) { uri = uri.with({ query: _uriMassage(uri.query) }); } - if (data) { - href = uri.toString(true); - } - return href; + return uri.toString(); }; // signal to code-block render that the @@ -77,6 +90,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) { @@ -96,6 +116,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 ( @@ -118,6 +144,10 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende } }; renderer.paragraph = (text): string => { + if (markdown.supportThemeIcons) { + const elements = renderCodicons(text); + text = elements.map(e => typeof e === 'string' ? e : e.outerHTML).join(''); + } return `

${text}

`; }; @@ -147,46 +177,89 @@ 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 (target.tagName !== 'A') { - target = target.parentElement; - if (!target || target.tagName !== 'A') { + [DOM.EventType.CLICK, DOM.EventType.AUXCLICK].forEach(event => { + actionHandler.disposeables.add(DOM.addDisposableListener(element, event, (e: MouseEvent) => { + const mouseEvent = new StandardMouseEvent(e); + if (!mouseEvent.leftButton && !mouseEvent.middleButton) { return; } - } - try { - const href = target.dataset['href']; - if (href) { - actionHandler.callback(href, event); + + let target: HTMLElement | null = mouseEvent.target; + if (target.tagName !== 'A') { + target = target.parentElement; + if (!target || target.tagName !== 'A') { + return; + } } - } catch (err) { - onUnexpectedError(err); - } finally { - event.preventDefault(); - } - })); + try { + const href = target.dataset['href']; + if (href) { + actionHandler.callback(href, mouseEvent); + } + } catch (err) { + onUnexpectedError(err); + } finally { + 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. + markedOptions.sanitizer = (html: string): string => { + const match = markdown.isTrusted ? html.match(/^()|(<\/\s*span>)$/) : undefined; + return match ? html : ''; }; + markedOptions.sanitize = true; + markedOptions.renderer = renderer; - const allowedSchemes = [Schemas.http, Schemas.https, Schemas.mailto, Schemas.data, Schemas.file, Schemas.vscodeRemote]; + const allowedSchemes = [Schemas.http, Schemas.https, Schemas.mailto, Schemas.data, Schemas.file, Schemas.vscodeRemote, Schemas.vscodeRemoteResource]; if (markdown.isTrusted) { allowedSchemes.push(Schemas.command); } - const renderedMarkdown = marked.parse(markdown.value, markedOptions); + // values that are too long will freeze the UI + let value = markdown.value ?? ''; + if (value.length > 100_000) { + value = `${value.substr(0, 100_000)}…`; + } + const renderedMarkdown = marked.parse( + markdown.supportThemeIcons ? markdownEscapeEscapedCodicons(value) : value, + markedOptions + ); + + function filter(token: { tag: string, attrs: { readonly [key: string]: string } }): boolean { + if (token.tag === 'span' && markdown.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; + } + element.innerHTML = insane(renderedMarkdown, { 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'], - 'iframe': ['allowfullscreen', 'frameborder', 'src'], 'img': ['src', 'title', 'alt', 'width', 'height'], - 'div': ['class', 'data-code'] - } + 'div': ['class', 'data-code'], + 'span': ['class', 'style'], + // https://github.com/microsoft/vscode/issues/95937 + 'th': ['align'], + 'td': ['align'] + }, + filter }); signalInnerHTML!(); diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index f3f2089100bcf..393bcded078d9 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -12,6 +12,7 @@ export interface IMouseEvent { readonly leftButton: boolean; readonly middleButton: boolean; readonly rightButton: boolean; + readonly buttons: number; readonly target: HTMLElement; readonly detail: number; readonly posx: number; @@ -33,6 +34,7 @@ export class StandardMouseEvent implements IMouseEvent { public readonly leftButton: boolean; public readonly middleButton: boolean; public readonly rightButton: boolean; + public readonly buttons: number; public readonly target: HTMLElement; public detail: number; public readonly posx: number; @@ -49,6 +51,7 @@ export class StandardMouseEvent implements IMouseEvent { this.leftButton = e.button === 0; this.middleButton = e.button === 1; this.rightButton = e.button === 2; + this.buttons = e.buttons; this.target = e.target; @@ -77,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(); } } @@ -205,17 +204,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 bef90a94d4d07..7166c4ec3a9cf 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -33,6 +33,7 @@ export interface GestureEvent extends MouseEvent { translationY: number; pageX: number; pageY: number; + tapCount: number; } interface Touch { @@ -71,19 +72,27 @@ export class Gesture extends Disposable { private dispatched = false; private targets: HTMLElement[]; + private ignoreTargets: HTMLElement[]; private handle: IDisposable | null; private activeTouches: { [id: number]: TouchData; }; + private _lastSetTapCountTime: number; + + private static readonly CLEAR_TAP_COUNT_TIME = 400; // ms + + private constructor() { super(); this.activeTouches = {}; this.handle = null; this.targets = []; - this._register(DomUtils.addDisposableListener(document, 'touchstart', (e: TouchEvent) => this.onTouchStart(e))); + this.ignoreTargets = []; + this._lastSetTapCountTime = 0; + 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 { @@ -103,9 +112,28 @@ export class Gesture extends Disposable { }; } + public static ignoreTarget(element: HTMLElement): IDisposable { + if (!Gesture.isTouchDevice()) { + return Disposable.None; + } + if (!Gesture.INSTANCE) { + Gesture.INSTANCE = new Gesture(); + } + + Gesture.INSTANCE.ignoreTargets.push(element); + + return { + dispose: () => { + Gesture.INSTANCE.ignoreTargets = Gesture.INSTANCE.ignoreTargets.filter(t => t !== element); + } + }; + } + @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 { @@ -221,13 +249,36 @@ 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; return event; } private dispatchEvent(event: GestureEvent): void { + if (event.type === EventType.Tap) { + const currentTime = (new Date()).getTime(); + let setTapCount = 0; + if (currentTime - this._lastSetTapCountTime > Gesture.CLEAR_TAP_COUNT_TIME) { + setTapCount = 1; + } else { + setTapCount = 2; + } + + this._lastSetTapCountTime = currentTime; + event.tapCount = setTapCount; + } else if (event.type === EventType.Change || event.type === EventType.Contextmenu) { + // tap is canceled by scrolling or context menu + this._lastSetTapCountTime = 0; + } + + for (let i = 0; i < this.ignoreTargets.length; i++) { + if (event.initialTarget instanceof Node && this.ignoreTargets[i].contains(event.initialTarget)) { + return; + } + } + this.targets.forEach(target => { if (event.initialTarget instanceof Node && target.contains(event.initialTarget)) { target.dispatchEvent(event); 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 0000000000000..cd1f27f6b9bac --- /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 { $, addClasses, addDisposableListener, append, EventHelper, EventLike, EventType, removeClasses, 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) { + removeClasses(this.label, this.cssClass); + } + + if (this.options.icon) { + this.cssClass = this.getAction().class; + + if (this.label) { + this.label.classList.add('codicon'); + if (this.cssClass) { + addClasses(this.label, this.cssClass); + } + } + + 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 60979ab095dc8..79c2a927201ff 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; @@ -90,4 +94,5 @@ display: flex; align-items: center; justify-content: center; + margin-right: 10px; } diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 28e9c4b55ed71..c25a81b5f10f6 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, @@ -391,39 +25,31 @@ export interface ActionTrigger { 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; } -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: ActionTrigger; + private _actionIds: string[]; // View Items viewItems: IActionViewItem[]; @@ -435,26 +61,27 @@ 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; + readonly onDidCancel = this._onDidCancel.event; 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 = this.options.triggerKeys ?? { + keys: [KeyCode.Enter, KeyCode.Space], + keyDown: false + }; if (this.options.actionRunner) { this._actionRunner = this.options.actionRunner; @@ -466,6 +93,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; @@ -479,7 +107,7 @@ export class ActionBar extends Disposable implements IActionRunner { let previousKey: KeyCode; let nextKey: KeyCode; - switch (this.options.orientation) { + switch (this._orientation) { case ActionsOrientation.HORIZONTAL: previousKey = KeyCode.LeftArrow; nextKey = KeyCode.RightArrow; @@ -506,14 +134,14 @@ export class ActionBar extends Disposable implements IActionRunner { let eventHandled = true; if (event.equals(previousKey)) { - this.focusPrevious(); + eventHandled = this.focusPrevious(); } else if (event.equals(nextKey)) { - this.focusNext(); + eventHandled = this.focusNext(); } else if (event.equals(KeyCode.Escape)) { - this.cancel(); + 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 +159,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 +175,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 +206,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 +216,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 +247,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 +262,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 +285,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 +325,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); } @@ -719,7 +359,7 @@ export class ActionBar extends Disposable implements IActionRunner { if (selectFirst && typeof this.focusedItem === 'undefined') { // Focus the first enabled item - this.focusedItem = this.viewItems.length - 1; + this.focusedItem = -1; this.focusNext(); } else { if (index !== undefined) { @@ -730,7 +370,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 +379,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 +393,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 +408,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 +424,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this.updateFocus(true); + return true; } protected updateFocus(fromRight?: boolean, preventScroll?: boolean): void { @@ -813,15 +465,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 +473,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; + } - select(index: number): void { - this.selectBox.select(index); + firstIndexOfAction = i; + break; } - private registerListeners(): void { - this._register(this.selectBox.onDidSelect(e => { - this.actionRunner.run(this._action, this.getActionContext(e.selected, e.index)); - })); + if (firstIndexOfAction === -1) { + return []; } - protected getActionContext(option: string, index: number) { - return option; - } + actions = actions.slice(firstIndexOfAction); - 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 fc71827eafab4..641e5f339a31b 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; + } -/** - * 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 + // 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 { - insertMessage(statusContainer, msg, disableRepeat); + dom.clearNode(alertContainer); + insertMessage(alertContainer2, msg); } } -let repeatedTimes = 0; -let prevText: string | undefined = undefined; -function insertMessage(target: HTMLElement, msg: string, disableRepeat?: boolean): void { +/** + * Given the provided message, will make sure that it is read as status to screen readers. + */ +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 65b207f0b0406..50ad242acef1f 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 5ce81d72e6679..fd91a3cb62598 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 }); @@ -329,7 +337,7 @@ export class BreadcrumbsWidget { container.tabIndex = -1; container.setAttribute('role', 'listitem'); dom.addClasses(container, 'monaco-breadcrumb-item'); - const iconContainer = dom.$('.codicon.codicon-chevron-right'); + 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 243be1451cc80..0000000000000 --- 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 40ba72b70868a..0000000000000 --- 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 0d746558a4fa9..0000000000000 --- 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 69299e332fb92..6d6823524840a 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 de4d35ef320eb..0bb56d0806ad5 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() : null; + 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,33 +175,37 @@ 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 { @@ -200,7 +233,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 +249,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 43f665fbd006e..52cc64b8a369d 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; @@ -42,7 +43,7 @@ function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView { get maximumSize() { return view.maximumWidth; }, get minimumSize() { return view.minimumWidth; }, onDidChange: Event.map(view.onDidChange, e => e && e.width), - layout: size => view.layout(size, getHeight(), Orientation.HORIZONTAL) + layout: (size, offset) => view.layout(size, getHeight(), 0, offset) }; } @@ -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; @@ -81,7 +95,7 @@ export class CenteredViewLayout implements IDisposable { this.resizeMargins(); } } else { - this.view.layout(width, height, Orientation.HORIZONTAL); + this.view.layout(width, height, 0, 0); } this.didLayout = true; } @@ -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/check-dark.svg b/src/vs/base/browser/ui/checkbox/check-dark.svg deleted file mode 100644 index 865cc83c347af..0000000000000 --- a/src/vs/base/browser/ui/checkbox/check-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/checkbox/check-light.svg b/src/vs/base/browser/ui/checkbox/check-light.svg deleted file mode 100644 index e1a546660ed15..0000000000000 --- a/src/vs/base/browser/ui/checkbox/check-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/checkbox/checkbox.css b/src/vs/base/browser/ui/checkbox/checkbox.css index fa9aa082d3991..972e94f7175c6 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.css +++ b/src/vs/base/browser/ui/checkbox/checkbox.css @@ -44,10 +44,7 @@ background-size: 16px !important; } -.monaco-custom-checkbox.monaco-simple-checkbox.checked { - background: url('check-light.svg') center center no-repeat; -} - -.monaco-custom-checkbox.monaco-simple-checkbox.checked { - background: url('check-dark.svg') center center no-repeat; +/* hide check when unchecked */ +.monaco-custom-checkbox.monaco-simple-checkbox.unchecked: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 afc9cc616a0a8..e29d71d60b11c 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,23 @@ 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); + } + classes.push(this._checked ? 'checked' : 'unchecked'); + 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)); @@ -113,6 +126,8 @@ export class Checkbox extends Widget { ev.preventDefault(); }); + this.ignoreGesture(this.domNode); + this.onkeydown(this.domNode, (keyboardEvent) => { if (keyboardEvent.keyCode === KeyCode.Space || keyboardEvent.keyCode === KeyCode.Enter) { this.checked = !this._checked; @@ -157,6 +172,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; } @@ -166,6 +184,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'; } } @@ -190,7 +209,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' }); + this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, icon: Codicon.check, actionClassName: 'monaco-simple-checkbox' }); this.domNode = this.checkbox.domNode; @@ -218,7 +237,7 @@ export class SimpleCheckbox extends Widget { } protected applyStyles(): void { - this.domNode.style.color = this.styles.checkboxForeground ? this.styles.checkboxForeground.toString() : null; + this.domNode.style.color = this.styles.checkboxForeground ? this.styles.checkboxForeground.toString() : ''; this.domNode.style.backgroundColor = this.styles.checkboxBackground ? this.styles.checkboxBackground.toString() : ''; this.domNode.style.borderColor = this.styles.checkboxBorder ? this.styles.checkboxBorder.toString() : ''; } 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 1c899992d0bf8..0000000000000 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ /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. - *--------------------------------------------------------------------------------------------*/ - -@font-face { - font-family: "codicon"; - src: url("./codicon.ttf?f5d7d29fe5677c620f764a26698d7611") 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; - - /* Hack to get web rendering to align to pixel grid */ - transform: rotate(0); - -webkit-transform: rotate(0.1deg); - -moz-transform: rotate(0); -} - - -.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-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-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-string: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-breakpoint-conditional-unverified:before { content: "\eaa6" } -.codicon-breakpoint-conditional:before { content: "\eaa7" } -.codicon-breakpoint-data-unverified:before { content: "\eaa8" } -.codicon-breakpoint-data:before { content: "\eaa9" } -.codicon-breakpoint-log-unverified:before { content: "\eaaa" } -.codicon-breakpoint-log: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-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-current-and-breakpoint:before { content: "\eaca" } -.codicon-current:before { content: "\eacb" } -.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-selection:before { content: "\eb4f" } -.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: "\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 9726457a2352b..0000000000000 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/codiconLabel.ts b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts deleted file mode 100644 index e496fd3a164f1..0000000000000 --- a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts +++ /dev/null @@ -1,33 +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!./codicon/codicon'; -import 'vs/css!./codicon/codicon-animations'; -import { escape } from 'vs/base/common/strings'; - -function expand(text: string): string { - return text.replace(/\$\((([a-z0-9\-]+?)(~([a-z0-9\-]*?))?)\)/gi, (_match, _g1, name, _g3, animation) => { - return ``; - }); -} - -export function renderCodicons(label: string): string { - return expand(escape(label)); -} - -export class CodiconLabel { - - constructor( - private readonly _container: HTMLElement - ) { } - - set text(text: string) { - this._container.innerHTML = renderCodicons(text || ''); - } - - set title(title: string) { - this._container.title = title; - } -} 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 abfde40dede6f..667002f5b7c15 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/base/browser/ui/codicons/codicon/codicon-modifications.css b/src/vs/base/browser/ui/codicons/codicon/codicon-modifications.css new file mode 100644 index 0000000000000..950493dd6bed5 --- /dev/null +++ b/src/vs/base/browser/ui/codicons/codicon/codicon-modifications.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +.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 0000000000000..a0593f7cbff6c --- /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 0000000000000..bb7ce5a58291a Binary files /dev/null and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/codicons/codiconLabel.ts b/src/vs/base/browser/ui/codicons/codiconLabel.ts new file mode 100644 index 0000000000000..e21eb2381a4da --- /dev/null +++ b/src/vs/base/browser/ui/codicons/codiconLabel.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { reset } from 'vs/base/browser/dom'; +import { renderCodicons } from 'vs/base/browser/codicons'; + +export class CodiconLabel { + + constructor( + private readonly _container: HTMLElement + ) { } + + set text(text: string) { + reset(this._container, ...renderCodicons(text ?? '')); + } + + set title(title: string) { + this._container.title = title; + } +} 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 0000000000000..59dbdbe873acc --- /dev/null +++ b/src/vs/base/browser/ui/codicons/codiconStyles.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { createStyleSheet } from 'vs/base/browser/dom'; +import { RunOnceScheduler } from 'vs/base/common/async'; + +function initialize() { + let codiconStyleSheet = createStyleSheet(); + codiconStyleSheet.id = 'codiconStyles'; + + function updateAll() { + const rules = []; + for (let c of iconRegistry.all) { + rules.push(formatRule(c)); + } + codiconStyleSheet.textContent = rules.join('\n'); + } + + const delayer = new RunOnceScheduler(updateAll, 0); + iconRegistry.onDidRegister(() => delayer.schedule()); + delayer.schedule(); +} + +export function formatRule(c: Codicon) { + let def = c.definition; + while (def instanceof Codicon) { + def = def.definition; + } + return `.codicon-${c.id}:before { content: '${def.character}'; }`; +} + +initialize(); diff --git a/src/vs/base/browser/ui/contextview/contextview.css b/src/vs/base/browser/ui/contextview/contextview.css index af3ead17a6b2a..bb7ebbcfdb294 100644 --- a/src/vs/base/browser/ui/contextview/contextview.css +++ b/src/vs/base/browser/ui/contextview/contextview.css @@ -5,5 +5,14 @@ .context-view { position: absolute; - z-index: 2000; -} \ No newline at end of file + 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 098e8b03182f2..d4aa1a0bedea2 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -5,8 +5,16 @@ import 'vs/css!./contextview'; import * as DOM from 'vs/base/browser/dom'; +import * as platform from 'vs/base/common/platform'; import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; 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; @@ -36,7 +44,7 @@ export interface IDelegate { } export interface IContextViewProvider { - showContextView(delegate: IDelegate): void; + showContextView(delegate: IDelegate, container?: HTMLElement): void; hideContextView(): void; layout(): void; } @@ -102,31 +110,63 @@ 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' }); + this.shadowRoot.innerHTML = ` + + `; + this.shadowRoot.appendChild(this.view); + this.shadowRoot.appendChild(DOM.$('slot')); + } else { + this.container.appendChild(this.view); + } const toDisposeOnSetContainer = new DisposableStore(); @@ -156,6 +196,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 @@ -173,12 +215,16 @@ export class ContextView extends Disposable { } } + getViewElement(): HTMLElement { + return this.view; + } + layout(): void { if (!this.isVisible()) { return; } - if (this.delegate!.canRelayout === false) { + if (this.delegate!.canRelayout === false && !(platform.isIOS && BrowserFeatures.pointerEvents)) { this.hide(); return; } @@ -252,10 +298,11 @@ export class ContextView extends Disposable { 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'); + DOM.toggleClass(this.view, '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'; } @@ -292,3 +339,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 909fa7c8e780b..eb0c0837ee9f9 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/countBadge/countBadge.ts b/src/vs/base/browser/ui/countBadge/countBadge.ts index cb04c5099f351..95d56cd3ed3ef 100644 --- a/src/vs/base/browser/ui/countBadge/countBadge.ts +++ b/src/vs/base/browser/ui/countBadge/countBadge.ts @@ -8,6 +8,7 @@ import { $, append } from 'vs/base/browser/dom'; import { format } from 'vs/base/common/strings'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; +import { IThemable } from 'vs/base/common/styler'; export interface ICountBadgeOptions extends ICountBadgetyles { count?: number; @@ -26,7 +27,7 @@ const defaultOpts = { badgeForeground: Color.fromHex('#FFFFFF') }; -export class CountBadge { +export class CountBadge implements IThemable { private element: HTMLElement; private count: number = 0; diff --git a/src/vs/base/browser/ui/dialog/close-dark.svg b/src/vs/base/browser/ui/dialog/close-dark.svg deleted file mode 100644 index 7305a8f099ab2..0000000000000 --- a/src/vs/base/browser/ui/dialog/close-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/dialog/close-light.svg b/src/vs/base/browser/ui/dialog/close-light.svg deleted file mode 100644 index ecddcd665b580..0000000000000 --- a/src/vs/base/browser/ui/dialog/close-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 1ee1c9860ccea..0a0c821553b35 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ /** Dialog: Modal Block */ -.monaco-workbench .dialog-modal-block { +.monaco-dialog-modal-block { position: fixed; height: 100%; width: 100%; @@ -15,27 +15,28 @@ align-items: center; } -.monaco-workbench .dialog-modal-block.dimmed { +.monaco-dialog-modal-block.dimmed { background: rgba(0, 0, 0, 0.3); } /** Dialog: Container */ -.monaco-workbench .dialog-box { +.monaco-dialog-box { display: flex; flex-direction: column-reverse; width: min-content; min-width: 500px; - max-width: 90%; + max-width: 90vw; min-height: 75px; - padding: 5px; + padding: 10px; + transform: translate3d(0px, 0px, 0px); } /** Dialog: Title Actions Row */ -.monaco-workbench .dialog-box .dialog-toolbar-row { - padding-right: 1px; +.monaco-dialog-box .dialog-toolbar-row { + padding-bottom: 4px; } -.monaco-workbench .dialog-box .action-label { +.monaco-dialog-box .action-label { height: 16px; min-width: 16px; background-size: 16px; @@ -46,82 +47,28 @@ } -.monaco-workbench .dialog-box .dialog-close-action { - background: url('close-light.svg') center center no-repeat; -} - -.vs-dark .monaco-workbench .dialog-box .dialog-close-action, -.hc-black .monaco-workbench .dialog-box .dialog-close-action { - background: url('close-dark.svg') center center no-repeat; -} - /** Dialog: Message Row */ -.monaco-workbench .dialog-box .dialog-message-row { +.monaco-dialog-box .dialog-message-row { display: flex; flex-grow: 1; - padding: 10px 15px 20px; align-items: center; + padding: 0 10px; } -.monaco-workbench .dialog-box .dialog-message-row .dialog-icon { - flex: 0 0 40px; - height: 40px; +.monaco-dialog-box .dialog-message-row > .dialog-icon.codicon { + flex: 0 0 48px; + height: 48px; align-self: baseline; - background-position: center; - background-repeat: no-repeat; - background-size: 40px; -} - -.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { - background-image: url('pending.svg'); -} - -.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info { - background-image: url('info-light.svg'); -} - -.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning { - background-image: url('warning-light.svg'); -} - -.vs .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error { - background-image: url('error-light.svg'); -} - -.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { - background-image: url('pending-dark.svg'); -} - -.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info, -.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-info { - background-image: url('info-dark.svg'); -} - -.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning, -.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-warning { - background-image: url('warning-dark.svg'); -} - -.vs-dark .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error, -.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-error { - background-image: url('error-dark.svg'); -} - -.hc-black .monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { - background-image: url('pending-hc.svg'); -} - -.monaco-workbench .dialog-box .dialog-message-row .dialog-icon.icon-pending { - background-size: 30px; + font-size: 48px; } /** Dialog: Message Container */ -.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container { +.monaco-dialog-box .dialog-message-row .dialog-message-container { display: flex; flex-direction: column; overflow: hidden; text-overflow: ellipsis; - padding-left: 20px; + padding-left: 24px; user-select: text; -webkit-user-select: text; -ms-user-select: text; @@ -130,33 +77,35 @@ } /** Dialog: Message */ -.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container .dialog-message { +.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message { line-height: 22px; font-size: 18px; flex: 1; /* let the message always grow */ white-space: normal; word-wrap: break-word; /* never overflow long words, but break to next line */ - padding-bottom: 10px; + min-height: 48px; /* matches icon height */ + margin-bottom: 8px; + display: flex; + align-items: center; } /** Dialog: Details */ -.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container .dialog-message-detail { +.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-workbench .dialog-box .dialog-message-row .dialog-message-container .dialog-message a:focus { +.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message a:focus { outline-width: 1px; outline-style: solid; } -.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container .dialog-checkbox-row { +.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-checkbox-row { padding: 15px 0px 0px; display: flex; } -.monaco-workbench .dialog-box .dialog-message-row .dialog-message-container .dialog-checkbox-row .dialog-checkbox-message { +.monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-checkbox-row .dialog-checkbox-message { cursor: pointer; user-select: none; -webkit-user-select: none; @@ -164,7 +113,7 @@ } /** Dialog: Buttons Row */ -.monaco-workbench .dialog-box > .dialog-buttons-row { +.monaco-dialog-box > .dialog-buttons-row { display: flex; align-items: center; justify-content: flex-end; @@ -172,18 +121,19 @@ overflow: hidden; /* buttons row should never overflow */ } -.monaco-workbench .dialog-box > .dialog-buttons-row { +.monaco-dialog-box > .dialog-buttons-row { display: flex; white-space: nowrap; + padding: 20px 10px 10px; } /** Dialog: Buttons */ -.monaco-workbench .dialog-box > .dialog-buttons-row > .dialog-buttons { +.monaco-dialog-box > .dialog-buttons-row > .dialog-buttons { display: flex; overflow: hidden; } -.monaco-workbench .dialog-box > .dialog-buttons-row > .dialog-buttons > .monaco-button { +.monaco-dialog-box > .dialog-buttons-row > .dialog-buttons > .monaco-button { width: fit-content; width: -moz-fit-content; padding: 5px 10px; diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index cc3e823d5c44f..623aa9c00d0c8 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, removeNode, isAncestor, addDisposableListener, EventType } from 'vs/base/browser/dom'; +import { $, hide, show, EventHelper, clearNode, removeClasses, addClasses, removeNode, 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,6 +17,7 @@ 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'; export interface IDialogOptions { cancelId?: number; @@ -37,6 +38,9 @@ export interface IDialogStyles extends IButtonStyles, ISimpleCheckboxStyles { dialogBackground?: Color; dialogShadow?: Color; dialogBorder?: Color; + errorIconForeground?: Color; + warningIconForeground?: Color; + infoIconForeground?: Color; } interface ButtonMapEntry { @@ -44,8 +48,14 @@ interface ButtonMapEntry { 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; @@ -60,8 +70,10 @@ export class Dialog extends Disposable { constructor(private container: HTMLElement, private message: string, buttons: string[], private options: IDialogOptions) { super(); - this.modal = this.container.appendChild($(`.dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`)); - this.element = this.modal.appendChild($('.dialog-box')); + this.modal = this.container.appendChild($(`.monaco-dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`)); + this.shadowElement = this.modal.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 @@ -75,7 +87,8 @@ export class Dialog extends Disposable { if (this.options.detail) { const messageElement = messageContainer.appendChild($('.dialog-message')); - messageElement.innerText = this.message; + const messageTextElement = messageElement.appendChild($('.dialog-message-text')); + messageTextElement.innerText = this.message; } this.messageDetailElement = messageContainer.appendChild($('.dialog-message-detail')); @@ -97,6 +110,28 @@ export class Dialog extends Disposable { this.toolbarContainer = toolbarRowElement.appendChild($('.dialog-toolbar')); } + 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 { if (this.messageDetailElement) { this.messageDetailElement.innerText = message; @@ -199,29 +234,29 @@ export class Dialog extends Disposable { } })); - removeClasses(this.iconElement, 'icon-error', 'icon-warning', 'icon-info'); + removeClasses(this.iconElement, dialogErrorIcon.classNames, dialogWarningIcon.classNames, dialogInfoIcon.classNames, Codicon.loading.classNames); switch (this.options.type) { case 'error': - addClass(this.iconElement, 'icon-error'); + addClasses(this.iconElement, dialogErrorIcon.classNames); break; case 'warning': - addClass(this.iconElement, 'icon-warning'); + addClasses(this.iconElement, dialogWarningIcon.classNames); break; case 'pending': - addClass(this.iconElement, 'icon-pending'); + addClasses(this.iconElement, Codicon.loading.classNames, 'codicon-animation-spin'); break; case 'none': case 'info': case 'question': default: - addClass(this.iconElement, 'icon-info'); + addClasses(this.iconElement, dialogInfoIcon.classNames); break; } const actionBar = new ActionBar(this.toolbarContainer, {}); - const action = new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), 'dialog-close-action', true, () => { + const action = new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), dialogCloseIcon.classNames, true, () => { resolve({ button: this.options.cancelId || 0, checkboxChecked: this.checkbox ? this.checkbox.checked : undefined }); return Promise.resolve(); }); @@ -230,7 +265,7 @@ export class Dialog extends Disposable { this.applyStyles(); - this.element.setAttribute('aria-label', this.message); + this.element.setAttribute('aria-label', this.getAriaLabel()); show(this.element); // Focus first element @@ -247,10 +282,13 @@ export class Dialog extends Disposable { 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; + } + if (this.element) { this.element.style.color = fgColor; this.element.style.backgroundColor = bgColor; - this.element.style.boxShadow = shadowColor; this.element.style.border = border; if (this.buttonGroup) { @@ -260,7 +298,31 @@ export class Dialog extends Disposable { if (this.checkbox) { this.checkbox.style(style); } + + if (this.messageDetailElement && fgColor && bgColor) { + const messageDetailColor = Color.fromHex(fgColor).transparent(.9); + this.messageDetailElement.style.color = messageDetailColor.makeOpaque(Color.fromHex(bgColor)).toString(); + } + + if (this.iconElement) { + 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(); + } + } } + } } diff --git a/src/vs/base/browser/ui/dialog/error-dark.svg b/src/vs/base/browser/ui/dialog/error-dark.svg deleted file mode 100644 index efdc5f2ae2d2d..0000000000000 --- a/src/vs/base/browser/ui/dialog/error-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/dialog/error-light.svg b/src/vs/base/browser/ui/dialog/error-light.svg deleted file mode 100644 index d646c72c740e5..0000000000000 --- a/src/vs/base/browser/ui/dialog/error-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/dialog/info-dark.svg b/src/vs/base/browser/ui/dialog/info-dark.svg deleted file mode 100644 index bb851afdfe587..0000000000000 --- a/src/vs/base/browser/ui/dialog/info-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/dialog/info-light.svg b/src/vs/base/browser/ui/dialog/info-light.svg deleted file mode 100644 index 6faf670cccc40..0000000000000 --- a/src/vs/base/browser/ui/dialog/info-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/base/browser/ui/dialog/pending-dark.svg b/src/vs/base/browser/ui/dialog/pending-dark.svg deleted file mode 100644 index 5f38838116201..0000000000000 --- a/src/vs/base/browser/ui/dialog/pending-dark.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/dialog/pending-hc.svg b/src/vs/base/browser/ui/dialog/pending-hc.svg deleted file mode 100644 index c6d0ec7e29f17..0000000000000 --- a/src/vs/base/browser/ui/dialog/pending-hc.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/dialog/pending.svg b/src/vs/base/browser/ui/dialog/pending.svg deleted file mode 100644 index 47ce444bb2402..0000000000000 --- a/src/vs/base/browser/ui/dialog/pending.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/vs/base/browser/ui/dialog/warning-dark.svg b/src/vs/base/browser/ui/dialog/warning-dark.svg deleted file mode 100644 index a267963e58554..0000000000000 --- a/src/vs/base/browser/ui/dialog/warning-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/dialog/warning-light.svg b/src/vs/base/browser/ui/dialog/warning-light.svg deleted file mode 100644 index f2e2aa741e558..0000000000000 --- a/src/vs/base/browser/ui/dialog/warning-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/base/browser/ui/dropdown/dropdown.css b/src/vs/base/browser/ui/dropdown/dropdown.css index 29ed0b73d7d60..fb87b8c5df102 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.css +++ b/src/vs/base/browser/ui/dropdown/dropdown.css @@ -5,12 +5,10 @@ .monaco-dropdown { height: 100%; - display: inline-block; padding: 0; } .monaco-dropdown > .dropdown-label { - display: inline-block; cursor: pointer; height: 100%; } diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index c36015f710b76..c90add134d693 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,7 +51,7 @@ 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]) { @@ -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 0000000000000..b9beb202aa6b1 --- /dev/null +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { 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 { BaseActionViewItem, 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'; +import { asArray } from 'vs/base/common/arrays'; + +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(/\W+/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(); + } + } +} diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 4c5392c87dc56..464ba3d3ffd07 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; @@ -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 => { @@ -301,6 +307,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 +330,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); diff --git a/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts b/src/vs/base/browser/ui/findinput/findInputCheckboxes.ts index b7ca14b0d7af9..07e80f78c7f12 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 5950e261de404..1e9d8194ccc3d 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; @@ -32,6 +33,7 @@ export interface IReplaceInputOptions extends IReplaceInputStyles { export interface IReplaceInputStyles extends IInputBoxStyles { inputActiveOptionBorder?: Color; + inputActiveOptionForeground?: Color; inputActiveOptionBackground?: Color; } @@ -42,10 +44,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 +65,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 +112,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; @@ -159,6 +164,7 @@ export class ReplaceInput extends Widget { appendTitle: '', isChecked: false, inputActiveOptionBorder: this.inputActiveOptionBorder, + inputActiveOptionForeground: this.inputActiveOptionForeground, inputActiveOptionBackground: this.inputActiveOptionBackground, })); this._register(this.preserveCase.onChange(viaKeyboard => { @@ -270,6 +276,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 +299,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); diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index c2448a2fbff46..db736d5da2335 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, @@ -212,6 +212,9 @@ 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; } + get element(): HTMLElement { return this.gridview.element; } private didLayout = false; @@ -604,8 +607,8 @@ export class SerializableGrid extends Grid { export type GridNodeDescriptor = { size?: number, groups?: GridNodeDescriptor[] }; export type GridDescriptor = { orientation: Orientation, groups?: GridNodeDescriptor[] }; -export function sanitizeGridNodeDescriptor(nodeDescriptor: GridNodeDescriptor): void { - if (nodeDescriptor.groups && nodeDescriptor.groups.length === 0) { +export function sanitizeGridNodeDescriptor(nodeDescriptor: GridNodeDescriptor, rootNode: boolean): void { + if (!rootNode && nodeDescriptor.groups && nodeDescriptor.groups.length <= 1) { nodeDescriptor.groups = undefined; } @@ -617,7 +620,7 @@ export function sanitizeGridNodeDescriptor(nodeDescriptor: GridNodeDescriptor): let totalDefinedSizeCount = 0; for (const child of nodeDescriptor.groups) { - sanitizeGridNodeDescriptor(child); + sanitizeGridNodeDescriptor(child, false); if (child.size) { totalDefinedSize += child.size; @@ -665,7 +668,7 @@ function getDimensions(node: ISerializedNode, orientation: Orientation): { width } export function createSerializedGrid(gridDescriptor: GridDescriptor): ISerializedGrid { - sanitizeGridNodeDescriptor(gridDescriptor); + sanitizeGridNodeDescriptor(gridDescriptor, true); const root = createSerializedNode(gridDescriptor); const { width, height } = getDimensions(root, gridDescriptor.orientation); diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 1c4944ce53910..b08b72a9479fc 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; @@ -30,8 +44,9 @@ export interface IView { readonly onDidChange: Event; readonly priority?: LayoutPriority; readonly snap?: boolean; - layout(width: number, height: number, orientation: Orientation): void; + layout(width: number, height: number, top: number, left: number): void; setVisible?(visible: boolean): void; + setBoundarySashes?(sashes: IBoundarySashes): void; } export interface ISerializableView extends IView { @@ -69,10 +84,10 @@ export function orthogonal(orientation: Orientation): Orientation { } export interface Box { - top: number; - left: number; - width: number; - height: number; + readonly top: number; + readonly left: number; + readonly width: number; + readonly height: number; } export interface GridLeafNode { @@ -117,11 +132,35 @@ export interface IGridViewOptions { readonly layoutController?: ILayoutController; } -class BranchNode implements ISplitView, IDisposable { +interface ILayoutContext { + readonly orthogonalSize: number; + readonly absoluteOffset: number; + readonly absoluteOrthogonalOffset: number; + readonly absoluteSize: number; + 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; readonly children: Node[] = []; - private splitview: SplitView; + private splitview: SplitView; private _size: number; get size(): number { return this._size; } @@ -129,6 +168,9 @@ class BranchNode implements ISplitView, IDisposable { private _orthogonalSize: number; get orthogonalSize(): number { return this._orthogonalSize; } + private absoluteOffset: number = 0; + private absoluteOrthogonalOffset: number = 0; + private _styles: IGridViewStyles; get styles(): IGridViewStyles { return this._styles; } @@ -140,6 +182,14 @@ class BranchNode implements ISplitView, IDisposable { return this.orientation === Orientation.HORIZONTAL ? this.orthogonalSize : this.size; } + get top(): number { + return this.orientation === Orientation.HORIZONTAL ? this.absoluteOffset : this.absoluteOrthogonalOffset; + } + + get left(): number { + return this.orientation === Orientation.HORIZONTAL ? this.absoluteOrthogonalOffset : this.absoluteOffset; + } + get minimumSize(): number { return this.children.length === 0 ? 0 : Math.max(...this.children.map(c => c.minimumOrthogonalSize)); } @@ -198,10 +248,27 @@ 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, + }; + } + } constructor( readonly orientation: Orientation, @@ -221,7 +288,7 @@ class BranchNode implements ISplitView, IDisposable { if (!childDescriptors) { // Normal behavior, we have no children yet, just set up the splitview this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout }); - this.splitview.layout(size, orthogonalSize); + this.splitview.layout(size, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize }); } else { // Reconstruction behavior, we want to reconstruct a splitview const descriptor = { @@ -241,9 +308,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], + }; }); } @@ -268,20 +341,32 @@ class BranchNode implements ISplitView, IDisposable { } } - layout(size: number, orthogonalSize: number | undefined): void { + layout(size: number, offset: number, ctx: ILayoutContext | undefined): void { if (!this.layoutController.isLayoutEnabled) { return; } - if (typeof orthogonalSize !== 'number') { + if (typeof ctx === 'undefined') { throw new Error('Invalid state'); } // branch nodes should flip the normal/orthogonal directions - this._size = orthogonalSize; + this._size = ctx.orthogonalSize; this._orthogonalSize = size; + this.absoluteOffset = ctx.absoluteOffset + offset; + this.absoluteOrthogonalOffset = ctx.absoluteOrthogonalOffset; + + this.splitview.layout(ctx.orthogonalSize, { + orthogonalSize: size, + absoluteOffset: this.absoluteOrthogonalOffset, + absoluteOrthogonalOffset: this.absoluteOffset, + absoluteSize: ctx.absoluteOrthogonalSize, + absoluteOrthogonalSize: ctx.absoluteSize + }); - this.splitview.layout(orthogonalSize, size); + // 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; } setVisible(visible: boolean): void { @@ -295,7 +380,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(); } @@ -304,15 +389,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] + }; } } @@ -332,11 +428,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; @@ -361,6 +463,8 @@ class BranchNode implements ISplitView, IDisposable { const child = this._removeChild(from); this._addChild(child, to); + + this.onDidChildrenChange(); } swapChildren(from: number, to: number): void { @@ -375,8 +479,15 @@ 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(); } resizeChild(index: number, size: number): void { @@ -511,7 +622,7 @@ class BranchNode implements ISplitView, IDisposable { } } -class LeafNode implements ISplitView, IDisposable { +class LeafNode implements ISplitView, IDisposable { private _size: number = 0; get size(): number { return this._size; } @@ -519,6 +630,9 @@ class LeafNode implements ISplitView, IDisposable { private _orthogonalSize: number; get orthogonalSize(): number { return this._orthogonalSize; } + private absoluteOffset: number = 0; + private absoluteOrthogonalOffset: number = 0; + readonly onDidSashReset: Event = Event.None; private _onDidLinkedWidthNodeChange = new Relay(); @@ -565,6 +679,14 @@ class LeafNode implements ISplitView, IDisposable { return this.orientation === Orientation.HORIZONTAL ? this.size : this.orthogonalSize; } + get top(): number { + return this.orientation === Orientation.HORIZONTAL ? this.absoluteOffset : this.absoluteOrthogonalOffset; + } + + get left(): number { + return this.orientation === Orientation.HORIZONTAL ? this.absoluteOrthogonalOffset : this.absoluteOffset; + } + get element(): HTMLElement { return this.view.element; } @@ -609,26 +731,30 @@ 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, orthogonalSize: number | undefined): void { + layout(size: number, offset: number, ctx: ILayoutContext | undefined): void { if (!this.layoutController.isLayoutEnabled) { return; } - if (typeof orthogonalSize !== 'number') { + if (typeof ctx === 'undefined') { throw new Error('Invalid state'); } this._size = size; - this._orthogonalSize = orthogonalSize; - this.view.layout(this.width, this.height, orthogonal(this.orientation)); + this._orthogonalSize = ctx.orthogonalSize; + this.absoluteOffset = ctx.absoluteOffset + offset; + this.absoluteOrthogonalOffset = ctx.absoluteOrthogonalOffset; + this.view.layout(this.width, this.height, this.top, this.left); } setVisible(visible: boolean): void { @@ -665,7 +791,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; @@ -715,7 +841,8 @@ export class GridView implements IDisposable { const { size, orthogonalSize } = this._root; this.root = flipNode(this._root, orthogonalSize, size); - this.root.layout(size, orthogonalSize); + 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; } @@ -729,6 +856,13 @@ 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); + } + /** * The first layout controller makes sure layout only propagates * to the views after the very first call to gridview.layout() @@ -771,7 +905,7 @@ export class GridView implements IDisposable { this.firstLayoutController.isLayoutEnabled = true; const [size, orthogonalSize] = this.root.orientation === Orientation.HORIZONTAL ? [height, width] : [width, height]; - this.root.layout(size, orthogonalSize); + this.root.layout(size, 0, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize }); } addView(view: IView, size: number | Sizing, location: number[]): void { @@ -850,6 +984,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; } @@ -1032,7 +1167,7 @@ export class GridView implements IDisposable { getView(location?: number[]): GridNode; getView(location?: number[]): GridNode { const node = location ? this.getNode(location)[1] : this._root; - return this._getViews(node, this.orientation, { top: 0, left: 0, width: this.width, height: this.height }); + return this._getViews(node, this.orientation); } static deserialize(json: ISerializedGridView, deserializer: IViewDeserializer, options: IGridViewOptions = {}): GridView { @@ -1042,6 +1177,8 @@ export class GridView implements IDisposable { throw new Error('Invalid JSON: \'width\' property must be a number.'); } else if (typeof json.height !== 'number') { throw new Error('Invalid JSON: \'height\' property must be a number.'); + } else if (json.root?.type !== 'branch') { + throw new Error('Invalid JSON: \'root\' property must have \'type\' value of branch.'); } const orientation = json.orientation; @@ -1076,24 +1213,20 @@ export class GridView implements IDisposable { return result; } - private _getViews(node: Node, orientation: Orientation, box: Box, cachedVisibleSize?: number): GridNode { + private _getViews(node: Node, orientation: Orientation, cachedVisibleSize?: number): GridNode { + const box = { top: node.top, left: node.left, width: node.width, height: node.height }; + if (node instanceof LeafNode) { return { view: node.view, box, cachedVisibleSize }; } const children: GridNode[] = []; - let i = 0; - let offset = 0; - - for (const child of node.children) { - const childOrientation = orthogonal(orientation); - const childBox: Box = orientation === Orientation.HORIZONTAL - ? { top: box.top, left: box.left + offset, width: child.width, height: box.height } - : { top: box.top + offset, left: box.left, width: box.width, height: child.height }; - const cachedVisibleSize = node.getChildCachedVisibleSize(i++); - - children.push(this._getViews(child, childOrientation, childBox, cachedVisibleSize)); - offset += orientation === Orientation.HORIZONTAL ? child.width : child.height; + + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + const cachedVisibleSize = node.getChildCachedVisibleSize(i); + + children.push(this._getViews(child, orthogonal(orientation), cachedVisibleSize)); } return { children, box }; diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 701d3a6a7858c..c6b9850857e02 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -4,17 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as objects from 'vs/base/common/objects'; -import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; -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; end: number; + extraClasses?: string; } export class HighlightedLabel { - private domNode: HTMLElement; + private readonly domNode: HTMLElement; private text: string = ''; private title: string = ''; private highlights: IHighlight[] = []; @@ -43,10 +44,6 @@ export class HighlightedLabel { return; } - if (!Array.isArray(highlights)) { - highlights = []; - } - this.text = text; this.title = title; this.highlights = highlights; @@ -55,7 +52,7 @@ export class HighlightedLabel { private render(): void { - let htmlContent = ''; + const children: HTMLSpanElement[] = []; let pos = 0; for (const highlight of this.highlights) { @@ -63,28 +60,31 @@ export class HighlightedLabel { continue; } if (pos < highlight.start) { - htmlContent += ''; const substring = this.text.substring(pos, highlight.start); - htmlContent += this.supportCodicons ? renderCodicons(substring) : escape(substring); - htmlContent += ''; + children.push(dom.$('span', undefined, ...this.supportCodicons ? renderCodicons(substring) : [substring])); pos = highlight.end; } - htmlContent += ''; + const substring = this.text.substring(highlight.start, highlight.end); - htmlContent += this.supportCodicons ? renderCodicons(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(substring) : escape(substring); - htmlContent += ''; + const substring = this.text.substring(pos,); + children.push(dom.$('span', undefined, ...this.supportCodicons ? renderCodicons(substring) : [substring])); } - this.domNode.innerHTML = htmlContent; - this.domNode.title = this.title; + dom.reset(this.domNode, ...children); + if (this.title) { + this.domNode.title = this.title; + } else { + this.domNode.removeAttribute('title'); + } this.didEverRender = true; } 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 0000000000000..9fbe9418b489b --- /dev/null +++ b/src/vs/base/browser/ui/hover/hover.css @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * 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; +} 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 0000000000000..6162569486e4b --- /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/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 69315ea919a24..de211c787a336 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -8,6 +8,8 @@ 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 { Range } from 'vs/base/common/range'; +import { equals } from 'vs/base/common/objects'; export interface IIconLabelCreationOptions { supportHighlights?: boolean; @@ -21,9 +23,12 @@ export interface IIconLabelValueOptions { hideIcon?: boolean; extraClasses?: string[]; italic?: boolean; + strikethrough?: boolean; matches?: IMatch[]; labelEscapeNewLines?: boolean; descriptionMatches?: IMatch[]; + readonly separator?: string; + readonly domId?: string; } class FastLabelNode { @@ -86,9 +91,12 @@ class FastLabelNode { } export class IconLabel extends Disposable { + private domNode: FastLabelNode; - private labelDescriptionContainer: FastLabelNode; - private labelNode: FastLabelNode | HighlightedLabel; + + private nameNode: Label | LabelWithHighlights; + + private descriptionContainer: FastLabelNode; private descriptionNode: FastLabelNode | HighlightedLabel | undefined; private descriptionNodeFactory: () => FastLabelNode | HighlightedLabel; @@ -97,18 +105,21 @@ export class IconLabel extends Disposable { this.domNode = this._register(new FastLabelNode(dom.append(container, dom.$('.monaco-icon-label')))); - this.labelDescriptionContainer = this._register(new FastLabelNode(dom.append(this.domNode.element, dom.$('.monaco-icon-label-description-container')))); + const labelContainer = dom.append(this.domNode.element, dom.$('.monaco-icon-label-container')); + + const nameContainer = dom.append(labelContainer, dom.$('span.monaco-icon-name-container')); + this.descriptionContainer = this._register(new FastLabelNode(dom.append(labelContainer, dom.$('span.monaco-icon-description-container')))); if (options?.supportHighlights) { - this.labelNode = new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')), !!options.supportCodicons); + this.nameNode = new LabelWithHighlights(nameContainer, !!options.supportCodicons); } else { - this.labelNode = this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')))); + this.nameNode = new Label(nameContainer); } if (options?.supportDescriptionHighlights) { - this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')), !!options.supportCodicons); + this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.descriptionContainer.element, dom.$('span.label-description')), !!options.supportCodicons); } else { - this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')))); + this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.descriptionContainer.element, dom.$('span.label-description')))); } } @@ -116,7 +127,7 @@ export class IconLabel extends Disposable { return this.domNode.element; } - setLabel(label?: string, description?: string, options?: IIconLabelValueOptions): void { + setLabel(label: string | string[], description?: string, options?: IIconLabelValueOptions): void { const classes = ['monaco-icon-label']; if (options) { if (options.extraClasses) { @@ -126,16 +137,16 @@ 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 || ''; - if (this.labelNode instanceof HighlightedLabel) { - this.labelNode.set(label || '', options?.matches, options?.title, options?.labelEscapeNewLines); - } else { - this.labelNode.textContent = label || ''; - } + this.nameNode.setLabel(label, options); if (description || this.descriptionNode) { if (!this.descriptionNode) { @@ -157,3 +168,116 @@ export class IconLabel extends Disposable { } } } + +class Label { + + private label: string | string[] | undefined = undefined; + private singleLabel: HTMLElement | undefined = undefined; + private options: IIconLabelValueOptions | undefined; + + constructor(private container: HTMLElement) { } + + setLabel(label: string | string[], options?: IIconLabelValueOptions): void { + if (this.label === label && equals(this.options, options)) { + return; + } + + this.label = label; + this.options = options; + + if (typeof label === 'string') { + if (!this.singleLabel) { + this.container.innerText = ''; + dom.removeClass(this.container, 'multiple'); + this.singleLabel = dom.append(this.container, dom.$('a.label-name', { id: options?.domId })); + } + + this.singleLabel.textContent = label; + } else { + this.container.innerText = ''; + dom.addClass(this.container, '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, 'role': 'treeitem' }, l)); + + if (i < label.length - 1) { + dom.append(this.container, dom.$('span.label-separator', undefined, options?.separator || '/')); + } + } + } + } +} + +function splitMatches(labels: string[], separator: string, matches: IMatch[] | undefined): IMatch[][] | undefined { + if (!matches) { + return undefined; + } + + let labelStart = 0; + + return labels.map(label => { + const labelRange = { start: labelStart, end: labelStart + label.length }; + + const result = matches + .map(match => Range.intersect(labelRange, match)) + .filter(range => !Range.isEmpty(range)) + .map(({ start, end }) => ({ start: start - labelStart, end: end - labelStart })); + + labelStart = labelRange.end + separator.length; + return result; + }); +} + +class LabelWithHighlights { + + private label: string | string[] | undefined = undefined; + private singleLabel: HighlightedLabel | undefined = undefined; + private options: IIconLabelValueOptions | undefined; + + constructor(private container: HTMLElement, private supportCodicons: boolean) { } + + setLabel(label: string | string[], options?: IIconLabelValueOptions): void { + if (this.label === label && equals(this.options, options)) { + return; + } + + this.label = label; + this.options = options; + + if (typeof label === 'string') { + if (!this.singleLabel) { + this.container.innerText = ''; + dom.removeClass(this.container, '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); + } else { + + this.container.innerText = ''; + dom.addClass(this.container, 'multiple'); + this.singleLabel = undefined; + + const separator = options?.separator || '/'; + const matches = splitMatches(label, separator, options?.matches); + + for (let i = 0; i < label.length; i++) { + const l = label[i]; + 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, 'role': 'treeitem' }); + const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), this.supportCodicons); + highlightedLabel.set(l, m, options?.title, 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 ad21f0ae1e4d9..3175fd3495941 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -31,48 +31,59 @@ flex-shrink: 0; /* fix for https://github.com/Microsoft/vscode/issues/13787 */ } -.monaco-icon-label > .monaco-icon-label-description-container { - overflow: hidden; /* this causes the label/description to shrink first if decorations are enabled */ +.monaco-icon-label > .monaco-icon-label-container { + min-width: 0; + overflow: hidden; text-overflow: ellipsis; + flex: 1; } -.monaco-icon-label > .monaco-icon-label-description-container > .label-name { +.monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container > .label-name { color: inherit; white-space: pre; /* enable to show labels that include multiple whitespaces */ } -.monaco-icon-label > .monaco-icon-label-description-container > .label-description { +.monaco-icon-label > .monaco-icon-label-container > .monaco-icon-name-container > .label-name > .label-separator { + margin: 0 2px; + opacity: 0.5; +} + +.monaco-icon-label > .monaco-icon-label-container > .monaco-icon-description-container > .label-description { opacity: .7; margin-left: 0.5em; font-size: 0.9em; white-space: pre; /* enable to show labels that include multiple whitespaces */ } -.monaco-icon-label.italic > .monaco-icon-label-description-container > .label-name, -.monaco-icon-label.italic > .monaco-icon-label-description-container > .label-description { +.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%; font-weight: 600; padding: 0 16px 0 5px; - margin-left: auto; text-align: center; } /* 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 59dd50015d898..2410c5234bb5d 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,13 +211,7 @@ 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); @@ -255,6 +248,10 @@ export class InputBox extends Widget { } } + public getAriaLabel(): string { + return this.ariaLabel; + } + public get mirrorElement(): HTMLElement | undefined { return this.mirror; } @@ -298,11 +295,16 @@ 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'); } public disable(): void { + this.blur(); this.input.disabled = true; this._hideMessage(); } @@ -366,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(); } @@ -395,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(); @@ -470,11 +460,11 @@ 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() : ''; - spanElement.style.color = styles.foreground ? styles.foreground.toString() : null; + spanElement.style.color = styles.foreground ? styles.foreground.toString() : ''; spanElement.style.border = styles.border ? `1px solid ${styles.border}` : ''; dom.append(div, spanElement); @@ -487,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'; } @@ -507,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(); @@ -527,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(); @@ -558,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 1bf13b4ce11b7..53857f4dad410 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -113,55 +113,24 @@ } .monaco-list-type-filter > .controls > * { + border: none; box-sizing: border-box; - width: 16px; - height: 16px; - margin: 0 0 0 2px; - flex-shrink: 0; -} - -.monaco-list-type-filter > .controls > .filter { -webkit-appearance: none; -moz-appearance: none; + background: none; width: 16px; height: 16px; - background: url("media/no-filter-light.svg"); - background-position: 50% 50%; - cursor: pointer; -} - -.monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-light.svg"); -} - -.vs-dark .monaco-list-type-filter > .controls > .filter { - background-image: url("media/no-filter-dark.svg"); -} - -.vs-dark .monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-dark.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .filter { - background-image: url("media/no-filter-hc.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-hc.svg"); -} - -.monaco-list-type-filter > .controls > .clear { - border: none; - background: url("media/close-light.svg"); + flex-shrink: 0; + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: center; cursor: pointer; } -.vs-dark .monaco-list-type-filter > .controls > .clear { - background-image: url("media/close-dark.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .clear { - background-image: url("media/close-hc.svg"); +.monaco-list-type-filter > .controls > .filter { + margin-left: 4px; } .monaco-list-type-filter-message { diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 076d78d77254c..25a09d22fae48 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -63,14 +63,6 @@ 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 { /** @@ -103,10 +95,11 @@ export const ListDragOverReactions = { export interface IListDragAndDrop { getDragURI(element: T): string | null; - getDragLabel?(elements: T[]): string | undefined; + getDragLabel?(elements: T[], originalEvent: DragEvent): string | undefined; onDragStart?(data: IDragAndDropData, originalEvent: DragEvent): void; onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IListDragOverReaction; drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void; + onDragEnd?(originalEvent: DragEvent): void; } export class ListError extends Error { @@ -128,6 +121,8 @@ export abstract class CachedListVirtualDelegate implements ILi abstract getTemplateId(element: T): string; setDynamicHeight(element: T, height: number): void { - this.cache.set(element, height); + if (height > 0) { + this.cache.set(element, height); + } } } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 352dd6b8e3480..03db9d2a60d72 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 620974cd757ed..eaa534d886c82 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,20 +79,29 @@ const DefaultOptions = { onDragOver() { return false; }, drop() { } }, - horizontalScrolling: false + horizontalScrolling: false, + transformOptimization: true }; -export class ElementsDragAndDropData implements IDragAndDropData { +export class ElementsDragAndDropData implements IDragAndDropData { readonly elements: T[]; + 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; } update(): void { } - getData(): any { + getData(): T[] { return this.elements; } } @@ -99,12 +116,12 @@ export class ExternalElementsDragAndDropData implements IDragAndDropData { update(): void { } - getData(): any { + getData(): T[] { return this.elements; } } -export class DesktopDragAndDropData implements IDragAndDropData { +export class NativeDragAndDropData implements IDragAndDropData { readonly types: any[]; readonly files: any[]; @@ -148,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; @@ -164,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); @@ -173,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; @@ -193,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, @@ -220,29 +303,35 @@ 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.willChange = 'transform'; + + 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, { + this.scrollable = new Scrollable(getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, cb => scheduleAtNextAnimationFrame(cb)); + this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: true, - horizontal: this.horizontalScrolling ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden, + 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); @@ -261,12 +350,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.'); @@ -364,7 +510,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; @@ -374,6 +520,7 @@ export class ListView implements ISpliceable, IDisposable { private eventuallyUpdateScrollWidth(): void { if (!this.horizontalScrolling) { + this.scrollableElementWidthDelayer.cancel(); return; } @@ -385,10 +532,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) { @@ -398,7 +541,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 { @@ -459,6 +602,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; @@ -482,7 +629,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) { @@ -502,7 +649,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) }); } } @@ -510,14 +657,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); @@ -530,10 +684,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`; } @@ -548,11 +705,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)); } } @@ -597,7 +754,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) { @@ -613,7 +770,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`; @@ -621,11 +781,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 { @@ -633,8 +793,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!); @@ -732,10 +892,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); @@ -766,14 +929,14 @@ export class ListView implements ISpliceable, IDisposable { let label: string | undefined; if (this.dnd.getDragLabel) { - label = this.dnd.getDragLabel(elements); + label = this.dnd.getDragLabel(elements, event); } if (typeof label === 'undefined') { 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); @@ -815,7 +978,7 @@ export class ListView implements ISpliceable, IDisposable { return false; } - this.currentDragData = new DesktopDragAndDropData(); + this.currentDragData = new NativeDragAndDropData(); } } @@ -843,13 +1006,9 @@ export class ListView implements ISpliceable, IDisposable { } // sanitize feedback list - feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort(); + feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort((a, b) => a - b); feedback = feedback[0] === -1 ? [-1] : feedback; - if (feedback.length === 0) { - throw new Error('Invalid empty feedback list'); - } - if (equalsDragFeedback(this.currentDragFeedback, feedback)) { return true; } @@ -858,15 +1017,19 @@ export class ListView implements ISpliceable, IDisposable { this.currentDragFeedbackDisposable.dispose(); if (feedback[0] === -1) { // entire list feedback - DOM.addClass(this.domNode, 'drop-target'); - this.currentDragFeedbackDisposable = toDisposable(() => DOM.removeClass(this.domNode, 'drop-target')); + this.domNode.classList.add('drop-target'); + this.rowsContainer.classList.add('drop-target'); + this.currentDragFeedbackDisposable = toDisposable(() => { + this.domNode.classList.remove('drop-target'); + this.rowsContainer.classList.remove('drop-target'); + }); } else { for (const index of feedback) { const item = this.items[index]!; item.dropTarget = true; if (item.row && item.row.domNode) { - DOM.addClass(item.row.domNode, 'drop-target'); + item.row.domNode.classList.add('drop-target'); } } @@ -876,7 +1039,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'); } } }); @@ -910,12 +1073,16 @@ export class ListView implements ISpliceable, IDisposable { this.dnd.drop(dragData, event.element, event.index, event.browserEvent); } - private onDragEnd(): void { + private onDragEnd(event: DragEvent): void { this.canDrop = false; this.teardownDragAndDropScrollTopAnimation(); this.clearDragOverFeedback(); this.currentDragData = undefined; StaticDND.CurrentDragAndDropData = undefined; + + if (this.dnd.onDragEnd) { + this.dnd.onDragEnd(event); + } } private clearDragOverFeedback(): void { @@ -928,8 +1095,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(); @@ -970,9 +1137,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) { @@ -1000,7 +1168,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 @@ -1066,7 +1234,7 @@ export class ListView implements ISpliceable, IDisposable { } } - if (typeof anchorElementIndex === 'number') { + if (updateScrollTop && typeof anchorElementIndex === 'number') { this.scrollTop = this.elementTop(anchorElementIndex) - anchorElementTopDelta!; } @@ -1083,7 +1251,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 48a5214a0d23f..39d1bbed07cc5 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,42 +698,35 @@ 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; } export class DefaultStyleController implements IStyleController { - constructor(private styleElement: HTMLStyleElement, private selectorSuffix?: string) { } + constructor(private styleElement: HTMLStyleElement, private selectorSuffix: string) { } style(styles: IListStyles): void { - const suffix = this.selectorSuffix ? `.${this.selectorSuffix}` : ''; + const suffix = this.selectorSuffix && `.${this.selectorSuffix}`; const content: string[] = []; + if (styles.listBackground) { + if (styles.listBackground.isOpaque()) { + content.push(`.monaco-list${suffix} .monaco-list-rows { background: ${styles.listBackground}; }`); + } else if (!platform.isMacintosh) { // subpixel AA doesn't exist in macOS + console.warn(`List with id '${this.selectorSuffix}' was styled with a non-opaque background color. This will break sub-pixel antialiasing.`); + } + } + if (styles.listFocusBackground) { content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { background-color: ${styles.listFocusBackground}; }`); content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused:hover { background-color: ${styles.listFocusBackground}; }`); // overwrite :hover style in this case! @@ -788,6 +803,7 @@ export class DefaultStyleController implements IStyleController { if (styles.listDropBackground) { content.push(` .monaco-list${suffix}.drop-target, + .monaco-list${suffix} .monaco-list-rows.drop-target, .monaco-list${suffix} .monaco-list-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } `); } @@ -808,40 +824,38 @@ 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'); } } -export interface IListOptions extends IListStyles { +export interface IListOptions { readonly identityProvider?: IIdentityProvider; readonly dnd?: IListDragAndDrop; readonly enableKeyboardNavigation?: boolean; 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?: IStyleController; - readonly accessibilityProvider?: IAccessibilityProvider; + readonly styleController?: (suffix: string) => IStyleController; + 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 { + listBackground?: Color; listFocusBackground?: Color; listFocusForeground?: Color; listActiveSelectionBackground?: Color; @@ -877,7 +891,7 @@ const defaultStyles: IListStyles = { treeIndentGuidesStroke: Color.fromHex('#a9a9a9') }; -const DefaultOptions = { +const DefaultOptions: IListOptions = { keyboardSupport: true, mouseSupport: true, multipleSelectionSupport: true, @@ -886,8 +900,7 @@ const DefaultOptions = { onDragStart(): void { }, onDragOver() { return false; }, drop() { } - }, - ariaRootRole: ListAriaRootRole.TREE + } }; // TODO@Joao: move these utils into a SortedArray class @@ -1019,7 +1032,7 @@ class AccessibiltyRenderer implements IListRenderer { templateId: string = 'a18n'; - constructor(private accessibilityProvider: IAccessibilityProvider) { } + constructor(private accessibilityProvider: IListAccessibilityProvider) { } renderTemplate(container: HTMLElement): HTMLElement { return container; @@ -1062,9 +1075,9 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { return this.dnd.getDragURI(element); } - getDragLabel?(elements: T[]): string | undefined { + getDragLabel?(elements: T[], originalEvent: DragEvent): string | undefined { if (this.dnd.getDragLabel) { - return this.dnd.getDragLabel(elements); + return this.dnd.getDragLabel(elements, originalEvent); } return undefined; @@ -1080,48 +1093,51 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { return this.dnd.onDragOver(data, targetElement, targetIndex, originalEvent); } + onDragEnd(originalEvent: DragEvent): void { + if (this.dnd.onDragEnd) { + this.dnd.onDragEnd(originalEvent); + } + } + drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void { this.dnd.drop(data, targetElement, targetIndex, originalEvent); } } -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 styleElement: HTMLStyleElement; private styleController: IStyleController; private typeLabelController?: TypeLabelController; + 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; } @@ -1178,15 +1194,22 @@ 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); const baseRenderers: IListRenderer[] = [this.focus.renderer, this.selection.renderer]; - if (_options.accessibilityProvider) { - baseRenderers.push(new AccessibiltyRenderer(_options.accessibilityProvider)); + this.accessibilityProvider = _options.accessibilityProvider; + + if (this.accessibilityProvider) { + baseRenderers.push(new AccessibiltyRenderer(this.accessibilityProvider)); + + if (this.accessibilityProvider.onDidChangeActiveDescendant) { + this.accessibilityProvider.onDidChangeActiveDescendant(this.onDidChangeActiveDescendant, this, this.disposables); + } } renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r])); @@ -1197,12 +1220,14 @@ export class List implements ISpliceable, IDisposable { }; this.view = new ListView(container, virtualDelegate, renderers, viewOptions); + this.view.domNode.setAttribute('role', role); - this.updateAriaRole(); - - this.styleElement = DOM.createStyleSheet(this.view.domNode); - - this.styleController = _options.styleController || new DefaultStyleController(this.styleElement, this.view.domId); + if (_options.styleController) { + this.styleController = _options.styleController(this.view.domId); + } else { + const styleElement = createStyleSheet(this.view.domNode); + this.styleController = new DefaultStyleController(styleElement, this.view.domId); + } this.spliceable = new CombinedSpliceable([ new TraitSpliceable(this.focus, this.view, _options.identityProvider), @@ -1231,16 +1256,18 @@ 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'); } - - this.style(_options); } protected createMouseController(options: IListOptions): MouseController { @@ -1253,6 +1280,8 @@ export class List implements ISpliceable, IDisposable { if (this.typeLabelController) { this.typeLabelController.updateOptions(this._options); } + + this.view.updateOptions(optionsUpdate); } get options(): IListOptions { @@ -1279,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(); } @@ -1287,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; } @@ -1331,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(); } @@ -1463,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); @@ -1573,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); } @@ -1603,40 +1633,38 @@ export class List implements ISpliceable, IDisposable { private _onFocusChange(): void { const focus = this.focus.get(); + this.view.domNode.classList.toggle('element-focused', focus.length > 0); + this.onDidChangeActiveDescendant(); + } - if (focus.length > 0) { - this.view.domNode.setAttribute('aria-activedescendant', this.view.getElementDomId(focus[0])); - } else { - this.view.domNode.removeAttribute('aria-activedescendant'); - } + private onDidChangeActiveDescendant(): void { + const focus = this.focus.get(); - this.updateAriaRole(); + if (focus.length > 0) { + let id: string | undefined; - DOM.toggleClass(this.view.domNode, 'element-focused', focus.length > 0); - } + if (this.accessibilityProvider?.getActiveDescendantId) { + id = this.accessibilityProvider.getActiveDescendantId(this.view.element(focus[0])); + } - private updateAriaRole(): void { - if (typeof this.options.ariaRole !== 'string') { - this.view.domNode.setAttribute('role', ListAriaRootRole.TREE); + this.view.domNode.setAttribute('aria-activedescendant', id || this.view.getElementDomId(focus[0])); } else { - this.view.domNode.setAttribute('role', this.options.ariaRole); + this.view.domNode.removeAttribute('aria-activedescendant'); } } 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/media/close-dark.svg b/src/vs/base/browser/ui/list/media/close-dark.svg deleted file mode 100644 index 7305a8f099ab2..0000000000000 --- a/src/vs/base/browser/ui/list/media/close-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/close-hc.svg b/src/vs/base/browser/ui/list/media/close-hc.svg deleted file mode 100644 index 7305a8f099ab2..0000000000000 --- a/src/vs/base/browser/ui/list/media/close-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/close-light.svg b/src/vs/base/browser/ui/list/media/close-light.svg deleted file mode 100644 index ecddcd665b580..0000000000000 --- a/src/vs/base/browser/ui/list/media/close-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-dark.svg b/src/vs/base/browser/ui/list/media/filter-dark.svg deleted file mode 100644 index 46c35f4374df7..0000000000000 --- a/src/vs/base/browser/ui/list/media/filter-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-hc.svg b/src/vs/base/browser/ui/list/media/filter-hc.svg deleted file mode 100644 index d7b6bdd392343..0000000000000 --- a/src/vs/base/browser/ui/list/media/filter-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-light.svg b/src/vs/base/browser/ui/list/media/filter-light.svg deleted file mode 100644 index 2550d80cb700f..0000000000000 --- a/src/vs/base/browser/ui/list/media/filter-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-dark.svg b/src/vs/base/browser/ui/list/media/no-filter-dark.svg deleted file mode 100644 index 6fc07d81a555c..0000000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-hc.svg b/src/vs/base/browser/ui/list/media/no-filter-hc.svg deleted file mode 100644 index 6fc07d81a555c..0000000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-light.svg b/src/vs/base/browser/ui/list/media/no-filter-light.svg deleted file mode 100644 index 3608b15d29ebb..0000000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 25eb321997bf6..953a11d13b460 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/check.svg b/src/vs/base/browser/ui/menu/check.svg deleted file mode 100644 index cea818ef5968a..0000000000000 --- a/src/vs/base/browser/ui/menu/check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/menu/ellipsis.svg b/src/vs/base/browser/ui/menu/ellipsis.svg deleted file mode 100644 index 2c52e359f610d..0000000000000 --- a/src/vs/base/browser/ui/menu/ellipsis.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - 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 77ed49872282b..0000000000000 --- a/src/vs/base/browser/ui/menu/menu.css +++ /dev/null @@ -1,211 +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%; - mask: url('submenu.svg') no-repeat 90% 50%/13px 13px; - -webkit-mask: url('submenu.svg') no-repeat 90% 50%/13px 13px; -} - -.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; - mask: url('check.svg') no-repeat 50% 56%/15px 15px; - -webkit-mask: url('check.svg') no-repeat 50% 56%/15px 15px; - width: 1em; - height: 100%; -} - -.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check { - visibility: visible; -} - -/* 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 > .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 { - background-position: center; - background-repeat: no-repeat; - background-size: 14px; - width: 20px; - height: 100%; -} - -.menubar.compact .toolbar-toggle-more { - background-position: center; - background-repeat: no-repeat; - background-size: 16px; - cursor: pointer; - width: 100%; - height: 100%; -} - -.menubar .toolbar-toggle-more { - display: inline-block; - padding: 0; - mask: url('ellipsis.svg') no-repeat 50% 55%/14px 14px; - -webkit-mask: url('ellipsis.svg') no-repeat 50% 55%/14px 14px; -} - -.menubar.compact .toolbar-toggle-more { - mask: url('menu.svg') no-repeat 50% 55%/16px 16px; - -webkit-mask: url('menu.svg') no-repeat 50% 55%/16px 16px; -} 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 1b61c97822951..0000000000000 --- 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 affb18ccbe13e..b845db410b8b0 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 } 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 } 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 { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor, hasClass, addDisposableListener, removeClass, append, $, addClasses, removeClasses, 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,6 +69,8 @@ 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'); @@ -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); @@ -198,18 +205,41 @@ export class Menu extends ActionBar { 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: BaseMenuActionViewItem, index: number, array: any[]) => { - item.updatePositionInSet(index + 1, array.length); + 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,41 @@ 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. + setTimeout(() => { + this.onClick(e); + }, 0); + })); + + this._register(addDisposableListener(this.element, EventType.CONTEXT_MENU, e => { EventHelper.stop(e, true); - this.onClick(e); })); }, 100); @@ -422,7 +481,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } } - this.check = append(this.item, $('span.menu-item-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 +523,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 +553,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(); - } } } @@ -593,16 +659,16 @@ class BaseMenuActionViewItem extends BaseActionViewItem { const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; - const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor; + const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : undefined; const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : ''; if (this.item) { - this.item.style.color = fgColor ? `${fgColor}` : null; - this.item.style.backgroundColor = bgColor ? `${bgColor}` : ''; + this.item.style.color = fgColor ? fgColor.toString() : ''; + this.item.style.backgroundColor = bgColor ? bgColor.toString() : ''; } if (this.check) { - this.check.style.backgroundColor = fgColor ? `${fgColor}` : ''; + this.check.style.color = fgColor ? fgColor.toString() : ''; } if (this.container) { @@ -644,7 +710,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); } @@ -661,8 +727,8 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { if (this.item) { addClass(this.item, 'monaco-submenu-item'); this.item.setAttribute('aria-haspopup', 'true'); - - this.submenuIndicator = append(this.item, $('span.submenu-indicator')); + this.updateAriaExpanded('false'); + this.submenuIndicator = append(this.item, $('span.submenu-indicator' + menuSubmenuIcon.cssSelector)); this.submenuIndicator.setAttribute('aria-hidden', 'true'); } @@ -678,7 +744,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 +764,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,9 +790,14 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { private cleanupExistingSubmenu(force: boolean): void { if (this.parentData.submenu && (force || (this.parentData.submenu !== this.mysubmenu))) { - this.parentData.submenu.dispose(); - this.parentData.submenu = undefined; + // 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) { this.submenuDisposables.clear(); this.submenuContainer = undefined; @@ -734,12 +805,40 @@ 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; } if (!this.parentData.submenu) { + this.updateAriaExpanded('true'); this.submenuContainer = append(this.element, $('div.monaco-submenu')); addClasses(this.submenuContainer, 'menubar-menu-items-holder', 'context-view'); @@ -747,29 +846,31 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { // 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); 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({ height: window.innerHeight, width: window.innerWidth }, 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); @@ -778,13 +879,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.parentData.parent.focus(); - if (this.parentData.submenu) { - this.parentData.submenu.dispose(); - this.parentData.submenu = undefined; - } - - this.submenuDisposables.clear(); - this.submenuContainer = undefined; + this.cleanupExistingSubmenu(true); } })); @@ -799,13 +894,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.submenuDisposables.add(this.parentData.submenu.onDidCancel(() => { this.parentData.parent.focus(); - if (this.parentData.submenu) { - this.parentData.submenu.dispose(); - this.parentData.submenu = undefined; - } - - this.submenuDisposables.clear(); - this.submenuContainer = undefined; + this.cleanupExistingSubmenu(true); })); this.parentData.submenu.focus(selectFirstItem); @@ -816,6 +905,12 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { } } + private updateAriaExpanded(value: string): void { + if (this.item) { + this.item?.setAttribute('aria-expanded', value); + } + } + protected applyStyle(): void { super.applyStyle(); @@ -827,7 +922,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; if (this.submenuIndicator) { - this.submenuIndicator.style.backgroundColor = fgColor ? `${fgColor}` : ''; + this.submenuIndicator.style.color = fgColor ? `${fgColor}` : ''; } if (this.parentData.submenu) { @@ -871,3 +966,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 0000000000000..d815cfeddb9a2 --- /dev/null +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * 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 .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 925ebc3a53b94..3a0c90d47e7a8 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -3,6 +3,7 @@ * 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'; @@ -10,8 +11,8 @@ 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'; @@ -21,9 +22,12 @@ 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 +35,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 +58,7 @@ export class MenuBar extends Disposable { buttonElement: HTMLElement; titleElement: HTMLElement; label: string; - actions?: ReadonlyArray; + actions?: IAction[]; }[]; private overflowMenu!: { @@ -88,7 +93,7 @@ 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(); @@ -135,8 +140,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(); } @@ -308,9 +313,10 @@ export class MenuBar extends Disposable { } createOverflowMenu(): void { - const label = this.options.compactMode !== undefined ? nls.localize('mAppMenu', 'Application Menu') : nls.localize('mMore', "..."); - const buttonElement = $('div.menubar-menu-button', { 'role': 'menuitem', 'tabindex': -1, 'aria-label': label, 'title': label, 'aria-haspopup': true }); - const titleElement = $('div.menubar-menu-title.toolbar-toggle-more', { 'role': 'none', 'aria-hidden': true }); + 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': 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); @@ -320,7 +326,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; @@ -418,9 +432,8 @@ export class MenuBar extends Disposable { DOM.removeNode(this.overflowMenu.titleElement); DOM.removeNode(this.overflowMenu.buttonElement); - if (this.overflowLayoutScheduled) { - this.overflowLayoutScheduled = dispose(this.overflowLayoutScheduled); - } + dispose(this.overflowLayoutScheduled); + this.overflowLayoutScheduled = undefined; } blur(): void { @@ -441,6 +454,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; @@ -482,7 +505,7 @@ 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) { @@ -490,6 +513,12 @@ export class MenuBar extends Disposable { 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.container.appendChild(this.overflowMenu.buttonElement); @@ -503,25 +532,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); @@ -560,7 +595,7 @@ export class MenuBar extends Disposable { if (!this.overflowLayoutScheduled) { this.overflowLayoutScheduled = DOM.scheduleAtNextAnimationFrame(() => { this.updateOverflowAction(); - this.overflowLayoutScheduled = null; + this.overflowLayoutScheduled = undefined; }); } @@ -910,7 +945,7 @@ export class MenuBar extends Disposable { return; } - const menuHolder = $('div.menubar-menu-items-holder'); + const menuHolder = $('div.menubar-menu-items-holder', { 'title': '' }); DOM.addClass(customMenu.buttonElement, 'open'); @@ -921,7 +956,6 @@ export class MenuBar extends Disposable { menuHolder.style.top = `0px`; 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`; @@ -934,7 +968,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)); diff --git a/src/vs/base/browser/ui/menu/submenu.svg b/src/vs/base/browser/ui/menu/submenu.svg deleted file mode 100644 index 98a0aa5924add..0000000000000 --- a/src/vs/base/browser/ui/menu/submenu.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - 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 0000000000000..2a998c4d97020 --- /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 0000000000000..8e5fd174cd8db --- /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.css b/src/vs/base/browser/ui/progressbar/progressbar.css index 5d1e8b3f76258..64368d699b0f1 100644 --- a/src/vs/base/browser/ui/progressbar/progressbar.css +++ b/src/vs/base/browser/ui/progressbar/progressbar.css @@ -35,7 +35,7 @@ animation-duration: 4s; animation-iteration-count: infinite; animation-timing-function: linear; - will-change: transform; + transform: translate3d(0px, 0px, 0px); } /** diff --git a/src/vs/base/browser/ui/sash/sash.css b/src/vs/base/browser/ui/sash/sash.css index 27be21169e7cb..e8ccd5521d2b1 100644 --- a/src/vs/base/browser/ui/sash/sash.css +++ b/src/vs/base/browser/ui/sash/sash.css @@ -13,13 +13,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 +25,6 @@ cursor: w-resize; } -.monaco-sash.horizontal { - cursor: ns-resize; - left: 0; - width: 100%; - height: 4px; -} - .monaco-sash.mac.horizontal { cursor: row-resize; } @@ -51,52 +37,11 @@ cursor: n-resize; } -.monaco-sash:not(.disabled).orthogonal-start::before, -.monaco-sash:not(.disabled).orthogonal-end::after { - content: ' '; - height: 8px; - width: 8px; - z-index: 100; - display: block; - cursor: all-scroll; - position: absolute; -} - -.monaco-sash.orthogonal-start.vertical::before { - left: -2px; - top: -4px; -} - -.monaco-sash.orthogonal-end.vertical::after { - left: -2px; - bottom: -4px; -} - -.monaco-sash.orthogonal-start.horizontal::before { - top: -2px; - left: -4px; -} - -.monaco-sash.orthogonal-end.horizontal::after { - top: -2px; - right: -4px; -} - .monaco-sash.disabled { cursor: default !important; pointer-events: none !important; } -/** Touch **/ - -.monaco-sash.touch.vertical { - width: 20px; -} - -.monaco-sash.touch.horizontal { - height: 20px; -} - /** Debug **/ .monaco-sash.debug { @@ -110,4 +55,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 7c2a52a89bfe8..da4a1a8796ad7 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,7 +238,7 @@ 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 @@ -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 4e9245ade07cc..23bfdb2c7bf82 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -20,6 +20,7 @@ import { INewScrollPosition, Scrollable, ScrollbarVisibility } from 'vs/base/com const MOUSE_DRAG_RESET_DISTANCE = 140; export interface ISimplifiedMouseEvent { + buttons: number; posx: number; posy: number; } @@ -60,6 +61,7 @@ export abstract class AbstractScrollbar extends Widget { this._scrollable = opts.scrollable; this._scrollbarState = opts.scrollbarState; this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName)); + this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()); this._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor()); this._shouldRender = true; this.domNode = createFastDomNode(document.createElement('div')); @@ -99,6 +101,7 @@ export abstract class AbstractScrollbar extends Widget { this.slider.setHeight(height); } this.slider.setLayerHinting(true); + this.slider.setContain('strict'); this.domNode.domNode.appendChild(this.slider.domNode); @@ -214,13 +217,15 @@ export abstract class AbstractScrollbar extends Widget { } } - private _sliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void { + private _sliderMouseDown(e: IMouseEvent, onDragFinished: () => void): void { const initialMousePosition = this._sliderMousePosition(e); const initialMouseOrthogonalPosition = this._sliderOrthogonalMousePosition(e); const initialScrollbarState = this._scrollbarState.clone(); this.slider.toggleClassName('active', true); this._mouseMoveMonitor.startMonitoring( + e.target, + e.buttons, standardMouseMoveMerger, (mouseMoveData: IStandardMouseMoveEventData) => { const mouseOrthogonalPosition = this._sliderOrthogonalMousePosition(mouseMoveData); @@ -254,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; @@ -262,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 1c36edb215ab5..6e7f132e99f83 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -9,17 +9,27 @@ 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 { constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { + const scrollDimensions = scrollable.getScrollDimensions(); + const scrollPosition = scrollable.getCurrentScrollPosition(); super({ lazyRender: options.lazyRender, host: host, scrollbarState: new ScrollbarState( (options.horizontalHasArrows ? options.arrowSize : 0), (options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize), - (options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize) + (options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize), + scrollDimensions.width, + scrollDimensions.scrollWidth, + scrollPosition.scrollLeft ), visibility: options.horizontal, extraScrollbarClassName: 'horizontal', @@ -31,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, @@ -42,7 +53,8 @@ export class HorizontalScrollbar extends AbstractScrollbar { }); this._createArrow({ - className: 'right-arrow', + className: 'scra', + icon: scrollbarButtonRightIcon, top: scrollbarDelta, left: undefined, bottom: undefined, @@ -87,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 23a6284928bcc..0000000000000 --- 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 cf127c6a09803..0000000000000 --- 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 8a5909bb262f7..0000000000000 --- 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 d4f475e4808cd..0000000000000 --- 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 61dddd673cbfd..0000000000000 --- 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 824671db551e1..0000000000000 --- 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 69a83f0f02a7e..0000000000000 --- 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 d2da965deed2f..0000000000000 --- 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 b05c77eed8a79..5d7a2dc705ab0 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'); - 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'); +.monaco-scrollable-element > .scrollbar > .scra { 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 f7216dfdb1073..00bb9830d54ff 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; @@ -64,7 +64,7 @@ export class MouseWheelClassifier { return false; } - // 0.5 * last + 0.25 * before last + 0.125 * before before last + ... + // 0.5 * last + 0.25 * 2nd last + 0.125 * 3rd last + ... let remainingInfluence = 1; let score = 0; let iteration = 1; @@ -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)); + this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { passive: false })); } } @@ -325,7 +349,14 @@ 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}`); @@ -334,6 +365,14 @@ export abstract class AbstractScrollableElement extends Widget { 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]; } @@ -507,6 +546,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 +600,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 7073bee8cb592..afb227be73b9f 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 b174952d7ae3f..ed645f8a06974 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts @@ -7,6 +7,8 @@ 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'; +import { addClasses } from 'vs/base/browser/dom'; /** * The arrow image size. @@ -16,6 +18,7 @@ export const ARROW_IMG_SIZE = 11; export interface ScrollbarArrowOptions { onActivate: () => void; className: string; + icon: Codicon; bgWidth: number; bgHeight: number; @@ -59,6 +62,8 @@ export class ScrollbarArrow extends Widget { this.domNode = document.createElement('div'); this.domNode.className = opts.className; + addClasses(this.domNode, opts.icon.classNames); + this.domNode.style.position = 'absolute'; this.domNode.style.width = ARROW_IMG_SIZE + 'px'; this.domNode.style.height = ARROW_IMG_SIZE + 'px'; @@ -93,6 +98,8 @@ export class ScrollbarArrow extends Widget { this._mousedownScheduleRepeatTimer.cancelAndSet(scheduleRepeater, 200); this._mouseMoveMonitor.startMonitoring( + e.target, + e.buttons, standardMouseMoveMerger, (mouseMoveData: IStandardMouseMoveEventData) => { /* Intentional empty */ diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts index edde3f8d1eed6..48e20a5a03379 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. @@ -62,14 +62,14 @@ export class ScrollbarState { private _computedSliderRatio: number; private _computedSliderPosition: number; - constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number) { + constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) { this._scrollbarSize = Math.round(scrollbarSize); this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize); this._arrowSize = Math.round(arrowSize); - this._visibleSize = 0; - this._scrollSize = 0; - this._scrollPosition = 0; + this._visibleSize = visibleSize; + this._scrollSize = scrollSize; + this._scrollPosition = scrollPosition; this._computedAvailableSize = 0; this._computedIsNeeded = false; @@ -81,11 +81,7 @@ export class ScrollbarState { } public clone(): ScrollbarState { - let r = new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize); - r.setVisibleSize(this._visibleSize); - r.setScrollSize(this._scrollSize); - r.setScrollPosition(this._scrollPosition); - return r; + return new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize, this._visibleSize, this._scrollSize, this._scrollPosition); } public setVisibleSize(visibleSize: number): boolean { @@ -118,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 8c1bb82823fe7..296913a3fd8b5 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -9,10 +9,16 @@ 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 { constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { + const scrollDimensions = scrollable.getScrollDimensions(); + const scrollPosition = scrollable.getCurrentScrollPosition(); super({ lazyRender: options.lazyRender, host: host, @@ -20,7 +26,10 @@ export class VerticalScrollbar extends AbstractScrollbar { (options.verticalHasArrows ? options.arrowSize : 0), (options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize), // give priority to vertical scroll bar over horizontal and let it scroll all the way to the bottom - 0 + 0, + scrollDimensions.height, + scrollDimensions.scrollHeight, + scrollPosition.scrollTop ), visibility: options.vertical, extraScrollbarClassName: 'vertical', @@ -32,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, @@ -43,7 +53,8 @@ export class VerticalScrollbar extends AbstractScrollbar { }); this._createArrow({ - className: 'down-arrow', + className: 'scra', + icon: scrollbarButtonDownIcon, top: undefined, left: scrollbarDelta, bottom: arrowDelta, @@ -88,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 f684dd1085c58..2997fd77e02c7 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.css +++ b/src/vs/base/browser/ui/selectBox/selectBox.css @@ -6,3 +6,22 @@ .monaco-select-box { width: 100%; } + +.monaco-select-box-dropdown-container { + font-size: 13px; + font-weight: normal; + text-transform: none; +} + +/** Actions */ + +.monaco-workbench .monaco-action-bar .action-item.select-container { + cursor: default; +} + +.monaco-workbench .monaco-action-bar .action-item .monaco-select-box { + cursor: pointer; + min-width: 110px; + min-height: 18px; + padding: 2px 23px 2px 8px; +} diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index c92601b96ba0f..79f0da0e6cd20 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -9,6 +9,7 @@ import { Event } from 'vs/base/common/event'; import { Widget } from 'vs/base/browser/ui/widget'; import { Color } from 'vs/base/common/color'; import { deepClone } from 'vs/base/common/objects'; +import { IContentActionHandler } from 'vs/base/browser/formattedTextRenderer'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { SelectBoxNative } from 'vs/base/browser/ui/selectBox/selectBoxNative'; @@ -39,6 +40,7 @@ export interface ISelectBoxOptions { useCustomDrawn?: boolean; ariaLabel?: string; minBottomMargin?: number; + optionsAsChildren?: boolean; } // Utilize optionItem interface to capture all option parameters @@ -47,6 +49,7 @@ export interface ISelectOptionItem { decoratorRight?: string; description?: string; descriptionIsMarkdown?: boolean; + descriptionMarkdownActionHandler?: IContentActionHandler; isDisabled?: boolean; } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.css b/src/vs/base/browser/ui/selectBox/selectBoxCustom.css index b717cde73f6c2..536a942235975 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 7e6ee0f7f6ae6..af72ef943e8c1 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -19,6 +19,8 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; 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.$; @@ -43,7 +45,7 @@ class SelectListRenderer implements IListRenderer; @@ -159,7 +162,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')); @@ -305,7 +308,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(); } @@ -353,10 +357,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) { @@ -364,7 +367,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(); } @@ -437,11 +440,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; @@ -452,15 +455,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) { @@ -469,6 +473,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } this._isVisible = false; + this.selectElement.setAttribute('aria-expanded', 'false'); if (focusSelect) { this.selectElement.focus(); @@ -498,42 +503,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; } @@ -551,7 +529,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); @@ -606,8 +584,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; @@ -616,8 +594,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; @@ -671,12 +649,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 { @@ -700,7 +680,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); } @@ -720,12 +700,20 @@ 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) => element.text, + 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')) @@ -748,10 +736,15 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi .filter(() => this.selectList.length > 0) .on(e => this.onMouseUp(e), this)); - - this._register(this.selectList.onDidBlur(_ => this.onListBlur())); 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)) { + return; + } + this.onListBlur(); + })); this.selectList.getHTMLElement().setAttribute('aria-label', this.selectBoxOptions.ariaLabel || ''); this.selectList.getHTMLElement().setAttribute('aria-expanded', 'true'); @@ -823,7 +816,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } - private renderDescriptionMarkdown(text: string): HTMLElement { + private renderDescriptionMarkdown(text: string, actionHandler?: IContentActionHandler): HTMLElement { const cleanRenderedMarkdown = (element: Node) => { for (let i = 0; i < element.childNodes.length; i++) { const child = element.childNodes.item(i); @@ -837,7 +830,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi } }; - const renderedMarkdown = renderMarkdown({ value: text }); + const renderedMarkdown = renderMarkdown({ value: text }, { actionHandler }); renderedMarkdown.classList.add('select-box-description-markdown'); cleanRenderedMarkdown(renderedMarkdown); @@ -852,14 +845,18 @@ 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; if (description) { if (descriptionIsMarkdown) { - this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description)); + const actionHandler = this.options[selectedIndex].descriptionMarkdownActionHandler; + this.selectionDetailsPane.appendChild(this.renderDescriptionMarkdown(description, actionHandler)); } else { this.selectionDetailsPane.innerText = description; } @@ -872,7 +869,6 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this._skipLayout = true; this.contextViewProvider.layout(); this._skipLayout = false; - } // List keyboard controller diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index 8c50b057fb53b..b6ed0f1238381 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -10,6 +10,7 @@ import * as dom from 'vs/base/browser/dom'; import * as arrays from 'vs/base/common/arrays'; import { ISelectBoxDelegate, ISelectOptionItem, ISelectBoxOptions, ISelectBoxStyles, ISelectData } from 'vs/base/browser/ui/selectBox/selectBox'; import { isMacintosh } from 'vs/base/common/platform'; +import { Gesture, EventType } from 'vs/base/browser/touch'; export class SelectBoxNative extends Disposable implements ISelectBoxDelegate { @@ -43,6 +44,16 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate { } private registerListeners() { + this._register(Gesture.addTarget(this.selectElement)); + [EventType.Tap].forEach(eventType => { + this._register(dom.addDisposableListener(this.selectElement, eventType, (e) => { + this.selectElement.focus(); + })); + }); + + 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; @@ -132,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/panelview.css b/src/vs/base/browser/ui/splitview/panelview.css deleted file mode 100644 index ff3554c4227fd..0000000000000 --- a/src/vs/base/browser/ui/splitview/panelview.css +++ /dev/null @@ -1,101 +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-panel-view { - width: 100%; - height: 100%; -} - -.monaco-panel-view .panel { - overflow: hidden; - width: 100%; - height: 100%; - display: flex; - flex-direction: column; -} - -.monaco-panel-view .panel > .panel-header { - font-size: 11px; - font-weight: bold; - text-transform: uppercase; - overflow: hidden; - display: flex; - cursor: pointer; - align-items: center; -} - -.monaco-panel-view .panel > .panel-header > .twisties { - width: 20px; - display: flex; - align-items: center; - justify-content: center; - transform-origin: center; - color: inherit; - flex-shrink: 0; -} - -.monaco-panel-view .panel > .panel-header.expanded > .twisties::before { - transform: rotate(90deg); -} - -/* TODO: actions should be part of the panel, but they aren't yet */ -.monaco-panel-view .panel > .panel-header > .actions { - display: none; - flex: 1; -} - -/* TODO: actions should be part of the panel, but they aren't yet */ -.monaco-panel-view .panel:hover > .panel-header.expanded > .actions, -.monaco-panel-view .panel > .panel-header.actions-always-visible.expanded > .actions, -.monaco-panel-view .panel > .panel-header.focused.expanded > .actions { - display: initial; -} - -/* TODO: actions should be part of the panel, but they aren't yet */ -.monaco-panel-view .panel > .panel-header > .actions .action-label.icon, -.monaco-panel-view .panel > .panel-header > .actions .action-label.codicon { - width: 28px; - height: 22px; - background-size: 16px; - background-position: center center; - background-repeat: no-repeat; - margin-right: 0; - display: flex; - align-items: center; - justify-content: center; - color: inherit; -} - -/* Bold font style does not go well with CJK fonts */ -.monaco-panel-view:lang(zh-Hans) .panel > .panel-header, -.monaco-panel-view:lang(zh-Hant) .panel > .panel-header, -.monaco-panel-view:lang(ja) .panel > .panel-header, -.monaco-panel-view:lang(ko) .panel > .panel-header { - font-weight: normal; -} - -.monaco-panel-view .panel > .panel-header.hidden { - display: none; -} - -.monaco-panel-view .panel > .panel-body { - overflow: hidden; - flex: 1; -} - -/* Animation */ - -.monaco-panel-view.animated .split-view-view { - transition-duration: 0.15s; - transition-timing-function: ease-out; -} - -.monaco-panel-view.animated.vertical .split-view-view { - transition-property: height; -} - -.monaco-panel-view.animated.horizontal .split-view-view { - transition-property: width; -} diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts deleted file mode 100644 index 85d24c6bc7c72..0000000000000 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ /dev/null @@ -1,495 +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!./panelview'; -import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -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 { 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'; - -export interface IPanelOptions { - ariaHeaderLabel?: string; - minimumBodySize?: number; - maximumBodySize?: number; - expanded?: boolean; -} - -export interface IPanelStyles { - dropBackground?: Color; - headerForeground?: Color; - headerBackground?: Color; - headerBorder?: Color; -} - -/** - * A Panel is a structured SplitView view. - * - * WARNING: You must call `render()` after you contruct it. - * It can't be done automatically at the end of the ctor - * because of the order of property initialization in TypeScript. - * Subclasses wouldn't be able to set own properties - * before the `render()` call, thus forbiding their use. - */ -export abstract class Panel extends Disposable implements IView { - - private static readonly HEADER_SIZE = 22; - - readonly element: HTMLElement; - private header!: HTMLElement; - private body!: HTMLElement; - - protected _expanded: boolean; - - private expandedSize: number | undefined = undefined; - private _headerVisible = true; - private _minimumBodySize: number; - private _maximumBodySize: number; - private ariaHeaderLabel: string; - private styles: IPanelStyles = {}; - private animationTimer: number | undefined = undefined; - - private readonly _onDidChange = this._register(new Emitter()); - readonly onDidChange: Event = this._onDidChange.event; - - private readonly _onDidChangeExpansionState = this._register(new Emitter()); - readonly onDidChangeExpansionState: Event = this._onDidChangeExpansionState.event; - - get draggableElement(): HTMLElement { - return this.header; - } - - get dropTargetElement(): HTMLElement { - return this.element; - } - - private _dropBackground: Color | undefined; - get dropBackground(): Color | undefined { - return this._dropBackground; - } - - get minimumBodySize(): number { - return this._minimumBodySize; - } - - set minimumBodySize(size: number) { - this._minimumBodySize = size; - this._onDidChange.fire(undefined); - } - - get maximumBodySize(): number { - return this._maximumBodySize; - } - - set maximumBodySize(size: number) { - this._maximumBodySize = size; - this._onDidChange.fire(undefined); - } - - private get headerSize(): number { - return this.headerVisible ? Panel.HEADER_SIZE : 0; - } - - get minimumSize(): number { - const headerSize = this.headerSize; - const expanded = !this.headerVisible || this.isExpanded(); - const minimumBodySize = expanded ? this._minimumBodySize : 0; - - return headerSize + minimumBodySize; - } - - get maximumSize(): number { - const headerSize = this.headerSize; - const expanded = !this.headerVisible || this.isExpanded(); - const maximumBodySize = expanded ? this._maximumBodySize : 0; - - return headerSize + maximumBodySize; - } - - width: number = 0; - - constructor(options: IPanelOptions = {}) { - super(); - this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; - this.ariaHeaderLabel = options.ariaHeaderLabel || ''; - this._minimumBodySize = typeof options.minimumBodySize === 'number' ? options.minimumBodySize : 120; - this._maximumBodySize = typeof options.maximumBodySize === 'number' ? options.maximumBodySize : Number.POSITIVE_INFINITY; - - this.element = $('.panel'); - } - - isExpanded(): boolean { - return this._expanded; - } - - setExpanded(expanded: boolean): boolean { - if (this._expanded === !!expanded) { - return false; - } - - this._expanded = !!expanded; - this.updateHeader(); - - if (expanded) { - if (typeof this.animationTimer === 'number') { - clearTimeout(this.animationTimer); - } - append(this.element, this.body); - } else { - this.animationTimer = window.setTimeout(() => { - this.body.remove(); - }, 200); - } - - this._onDidChangeExpansionState.fire(expanded); - this._onDidChange.fire(expanded ? this.expandedSize : undefined); - return true; - } - - get headerVisible(): boolean { - return this._headerVisible; - } - - set headerVisible(visible: boolean) { - if (this._headerVisible === !!visible) { - return; - } - - this._headerVisible = !!visible; - this.updateHeader(); - this._onDidChange.fire(undefined); - } - - render(): void { - this.header = $('.panel-header'); - append(this.element, this.header); - this.header.setAttribute('tabindex', '0'); - this.header.setAttribute('role', 'toolbar'); - 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.updateHeader(); - - const onHeaderKeyDown = Event.chain(domEvent(this.header, 'keydown')) - .map(e => new StandardKeyboardEvent(e)); - - this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space) - .event(() => this.setExpanded(!this.isExpanded()), null)); - - this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow) - .event(() => this.setExpanded(false), null)); - - this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow) - .event(() => this.setExpanded(true), null)); - - this._register(domEvent(this.header, 'click') - (() => this.setExpanded(!this.isExpanded()), null)); - - this.body = append(this.element, $('.panel-body')); - this.renderBody(this.body); - } - - layout(height: number): void { - const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; - - if (this.isExpanded()) { - this.layoutBody(height - headerSize, this.width); - this.expandedSize = height; - } - } - - style(styles: IPanelStyles): void { - this.styles = styles; - - if (!this.header) { - return; - } - - this.updateHeader(); - } - - 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.setAttribute('aria-expanded', String(expanded)); - - this.header.style.color = this.styles.headerForeground ? this.styles.headerForeground.toString() : null; - this.header.style.backgroundColor = this.styles.headerBackground ? this.styles.headerBackground.toString() : ''; - this.header.style.borderTop = this.styles.headerBorder ? `1px solid ${this.styles.headerBorder}` : ''; - this._dropBackground = this.styles.dropBackground; - } - - protected abstract renderHeader(container: HTMLElement): void; - protected abstract renderBody(container: HTMLElement): void; - protected abstract layoutBody(height: number, width: number): void; -} - -interface IDndContext { - draggable: PanelDraggable | null; -} - -class PanelDraggable 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 _onDidDrop = this._register(new Emitter<{ from: Panel, to: Panel }>()); - readonly onDidDrop = this._onDidDrop.event; - - constructor(private panel: Panel, private dnd: IPanelDndController, private context: IDndContext) { - super(); - - panel.draggableElement.draggable = true; - this._register(domEvent(panel.draggableElement, 'dragstart')(this.onDragStart, this)); - this._register(domEvent(panel.dropTargetElement, 'dragenter')(this.onDragEnter, this)); - this._register(domEvent(panel.dropTargetElement, 'dragleave')(this.onDragLeave, this)); - this._register(domEvent(panel.dropTargetElement, 'dragend')(this.onDragEnd, this)); - this._register(domEvent(panel.dropTargetElement, 'drop')(this.onDrop, this)); - } - - private onDragStart(e: DragEvent): void { - if (!this.dnd.canDrag(this.panel) || !e.dataTransfer) { - e.preventDefault(); - e.stopPropagation(); - return; - } - - e.dataTransfer.effectAllowed = 'move'; - - if (isFirefox) { - // Firefox: requires to set a text data transfer to get going - e.dataTransfer?.setData(DataTransfers.TEXT, this.panel.draggableElement.textContent || ''); - } - - const dragImage = append(document.body, $('.monaco-drag-image', {}, this.panel.draggableElement.textContent || '')); - e.dataTransfer.setDragImage(dragImage, -10, -10); - setTimeout(() => document.body.removeChild(dragImage), 0); - - this.context.draggable = this; - } - - private onDragEnter(e: DragEvent): void { - if (!this.context.draggable || this.context.draggable === this) { - return; - } - - if (!this.dnd.canDrop(this.context.draggable.panel, this.panel)) { - return; - } - - this.dragOverCounter++; - this.render(); - } - - private onDragLeave(e: DragEvent): void { - if (!this.context.draggable || this.context.draggable === this) { - return; - } - - if (!this.dnd.canDrop(this.context.draggable.panel, this.panel)) { - return; - } - - this.dragOverCounter--; - - if (this.dragOverCounter === 0) { - this.render(); - } - } - - private onDragEnd(e: DragEvent): void { - if (!this.context.draggable) { - return; - } - - this.dragOverCounter = 0; - this.render(); - this.context.draggable = null; - } - - private onDrop(e: DragEvent): void { - if (!this.context.draggable) { - return; - } - - EventHelper.stop(e); - - this.dragOverCounter = 0; - this.render(); - - if (this.dnd.canDrop(this.context.draggable.panel, this.panel) && this.context.draggable !== this) { - this._onDidDrop.fire({ from: this.context.draggable.panel, to: this.panel }); - } - - this.context.draggable = null; - } - - private render(): void { - let backgroundColor: string | null = null; - - if (this.dragOverCounter > 0) { - backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString(); - } - - this.panel.dropTargetElement.style.backgroundColor = backgroundColor || ''; - } -} - -export interface IPanelDndController { - canDrag(panel: Panel): boolean; - canDrop(panel: Panel, overPanel: Panel): boolean; -} - -export class DefaultPanelDndController implements IPanelDndController { - - canDrag(panel: Panel): boolean { - return true; - } - - canDrop(panel: Panel, overPanel: Panel): boolean { - return true; - } -} - -export interface IPanelViewOptions { - dnd?: IPanelDndController; -} - -interface IPanelItem { - panel: Panel; - disposable: IDisposable; -} - -export class PanelView extends Disposable { - - private dnd: IPanelDndController | undefined; - private dndContext: IDndContext = { draggable: null }; - private el: HTMLElement; - private panelItems: IPanelItem[] = []; - private width: number = 0; - private splitview: SplitView; - private animationTimer: number | undefined = undefined; - - private _onDidDrop = this._register(new Emitter<{ from: Panel, to: Panel }>()); - readonly onDidDrop: Event<{ from: Panel, to: Panel }> = this._onDidDrop.event; - - readonly onDidSashChange: Event; - - constructor(container: HTMLElement, options: IPanelViewOptions = {}) { - super(); - - this.dnd = options.dnd; - this.el = append(container, $('.monaco-panel-view')); - this.splitview = this._register(new SplitView(this.el)); - this.onDidSashChange = this.splitview.onDidSashChange; - } - - addPanel(panel: Panel, size: number, index = this.splitview.length): void { - const disposables = new DisposableStore(); - panel.onDidChangeExpansionState(this.setupAnimation, this, disposables); - - const panelItem = { panel, disposable: disposables }; - this.panelItems.splice(index, 0, panelItem); - panel.width = this.width; - this.splitview.addView(panel, size, index); - - if (this.dnd) { - const draggable = new PanelDraggable(panel, this.dnd, this.dndContext); - disposables.add(draggable); - disposables.add(draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop)); - } - } - - removePanel(panel: Panel): void { - const index = firstIndex(this.panelItems, item => item.panel === panel); - - if (index === -1) { - return; - } - - this.splitview.removeView(index); - const panelItem = this.panelItems.splice(index, 1)[0]; - panelItem.disposable.dispose(); - } - - movePanel(from: Panel, to: Panel): void { - const fromIndex = firstIndex(this.panelItems, item => item.panel === from); - const toIndex = firstIndex(this.panelItems, item => item.panel === to); - - if (fromIndex === -1 || toIndex === -1) { - return; - } - - const [panelItem] = this.panelItems.splice(fromIndex, 1); - this.panelItems.splice(toIndex, 0, panelItem); - - this.splitview.moveView(fromIndex, toIndex); - } - - resizePanel(panel: Panel, size: number): void { - const index = firstIndex(this.panelItems, item => item.panel === panel); - - if (index === -1) { - return; - } - - this.splitview.resizeView(index, size); - } - - getPanelSize(panel: Panel): number { - const index = firstIndex(this.panelItems, item => item.panel === panel); - - if (index === -1) { - return -1; - } - - return this.splitview.getViewSize(index); - } - - layout(height: number, width: number): void { - this.width = width; - - for (const panelItem of this.panelItems) { - panelItem.panel.width = width; - } - - this.splitview.layout(height); - } - - private setupAnimation(): void { - if (typeof this.animationTimer === 'number') { - window.clearTimeout(this.animationTimer); - } - - addClass(this.el, 'animated'); - - this.animationTimer = window.setTimeout(() => { - this.animationTimer = undefined; - removeClass(this.el, 'animated'); - }, 200); - } - - dispose(): void { - super.dispose(); - - this.panelItems.forEach(i => i.disposable.dispose()); - } -} diff --git a/src/vs/base/browser/ui/splitview/paneview.css b/src/vs/base/browser/ui/splitview/paneview.css new file mode 100644 index 0000000000000..ec0370aab3617 --- /dev/null +++ b/src/vs/base/browser/ui/splitview/paneview.css @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-pane-view { + width: 100%; + height: 100%; +} + +.monaco-pane-view .pane { + overflow: hidden; + width: 100%; + height: 100%; + display: flex; + 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; + overflow: hidden; + 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 { + width: 20px; + display: flex; + align-items: center; + justify-content: center; + transform-origin: center; + color: inherit; + 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); +} + +/* TODO: actions should be part of the pane, but they aren't yet */ +.monaco-pane-view .pane > .pane-header > .actions { + display: none; + flex: 1; +} + +/* TODO: actions should be part of the pane, but they aren't yet */ +.monaco-pane-view .pane:hover > .pane-header.expanded > .actions, +.monaco-pane-view .pane > .pane-header.actions-always-visible.expanded > .actions, +.monaco-pane-view .pane > .pane-header.focused.expanded > .actions { + display: initial; +} + +/* TODO: actions should be part of the pane, but they aren't yet */ +.monaco-pane-view .pane > .pane-header > .actions .action-label.icon, +.monaco-pane-view .pane > .pane-header > .actions .action-label.codicon { + width: 28px; + height: 22px; + background-size: 16px; + background-position: center center; + background-repeat: no-repeat; + margin-right: 0; + display: flex; + align-items: center; + justify-content: center; + 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, +.monaco-pane-view:lang(ja) .pane > .pane-header, +.monaco-pane-view:lang(ko) .pane > .pane-header { + font-weight: normal; +} + +.monaco-pane-view .pane > .pane-header.hidden { + display: none; +} + +.monaco-pane-view .pane > .pane-body { + overflow: hidden; + flex: 1; +} + +/* Animation */ + +.monaco-pane-view.animated .split-view-view { + transition-duration: 0.15s; + transition-timing-function: ease-out; +} + +.monaco-pane-view.animated.vertical .split-view-view { + transition-property: height; +} + +.monaco-pane-view.animated.horizontal .split-view-view { + transition-property: width; +} + +#monaco-workbench-pane-drop-overlay { + position: absolute; + z-index: 10000; + width: 100%; + height: 100%; + left: 0; + box-sizing: border-box; +} + +#monaco-workbench-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-workbench-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 new file mode 100644 index 0000000000000..7185380bf401b --- /dev/null +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -0,0 +1,576 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./paneview'; +import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +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, 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 { + minimumBodySize?: number; + maximumBodySize?: number; + expanded?: boolean; + orientation?: Orientation; + title: string; + titleDescription?: string; +} + +export interface IPaneStyles { + dropBackground?: Color; + headerForeground?: Color; + headerBackground?: Color; + headerBorder?: Color; + leftBorder?: Color; +} + +/** + * A Pane is a structured SplitView view. + * + * WARNING: You must call `render()` after you contruct it. + * It can't be done automatically at the end of the ctor + * because of the order of property initialization in TypeScript. + * Subclasses wouldn't be able to set own properties + * before the `render()` call, thus forbiding their use. + */ +export abstract class Pane extends Disposable implements IView { + + private static readonly HEADER_SIZE = 22; + + readonly element: HTMLElement; + private header!: HTMLElement; + private body!: HTMLElement; + + protected _expanded: boolean; + protected _orientation: Orientation; + + private expandedSize: number | undefined = undefined; + private _headerVisible = true; + private _minimumBodySize: number; + private _maximumBodySize: number; + private ariaHeaderLabel: string; + private styles: IPaneStyles = {}; + private animationTimer: number | undefined = undefined; + + private readonly _onDidChange = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + private readonly _onDidChangeExpansionState = this._register(new Emitter()); + readonly onDidChangeExpansionState: Event = this._onDidChangeExpansionState.event; + + get draggableElement(): HTMLElement { + return this.header; + } + + get dropTargetElement(): HTMLElement { + return this.element; + } + + private _dropBackground: Color | undefined; + get dropBackground(): Color | undefined { + return this._dropBackground; + } + + get minimumBodySize(): number { + return this._minimumBodySize; + } + + set minimumBodySize(size: number) { + this._minimumBodySize = size; + this._onDidChange.fire(undefined); + } + + get maximumBodySize(): number { + return this._maximumBodySize; + } + + set maximumBodySize(size: number) { + this._maximumBodySize = size; + this._onDidChange.fire(undefined); + } + + private get headerSize(): number { + return this.headerVisible ? Pane.HEADER_SIZE : 0; + } + + get minimumSize(): number { + const headerSize = this.headerSize; + const expanded = !this.headerVisible || this.isExpanded(); + const minimumBodySize = expanded ? this.minimumBodySize : 0; + + return headerSize + minimumBodySize; + } + + get maximumSize(): number { + const headerSize = this.headerSize; + const expanded = !this.headerVisible || this.isExpanded(); + const maximumBodySize = expanded ? this.maximumBodySize : 0; + + return headerSize + maximumBodySize; + } + + orthogonalSize: number = 0; + + constructor(options: IPaneOptions) { + super(); + this._expanded = typeof options.expanded === 'undefined' ? true : !!options.expanded; + 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'); + } + + isExpanded(): boolean { + return this._expanded; + } + + setExpanded(expanded: boolean): boolean { + if (this._expanded === !!expanded) { + return false; + } + + if (this.element) { + this.element.classList.toggle('expanded', expanded); + } + + this._expanded = !!expanded; + this.updateHeader(); + + if (expanded) { + if (typeof this.animationTimer === 'number') { + clearTimeout(this.animationTimer); + } + append(this.element, this.body); + } else { + this.animationTimer = window.setTimeout(() => { + this.body.remove(); + }, 200); + } + + this._onDidChangeExpansionState.fire(expanded); + this._onDidChange.fire(expanded ? this.expandedSize : undefined); + return true; + } + + get headerVisible(): boolean { + return this._headerVisible; + } + + set headerVisible(visible: boolean) { + if (this._headerVisible === !!visible) { + return; + } + + this._headerVisible = !!visible; + this.updateHeader(); + 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'); + // 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(() => 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)); + + this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.Enter || e.keyCode === KeyCode.Space) + .event(() => this.setExpanded(!this.isExpanded()), null)); + + this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow) + .event(() => this.setExpanded(false), null)); + + this._register(onHeaderKeyDown.filter(e => e.keyCode === KeyCode.RightArrow) + .event(() => this.setExpanded(true), null)); + + this._register(domEvent(this.header, 'click') + (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(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.body.classList.toggle('wide', width >= 600); + this.layoutBody(height, width); + this.expandedSize = size; + } + } + + style(styles: IPaneStyles): void { + this.styles = styles; + + if (!this.header) { + return; + } + + this.updateHeader(); + } + + protected updateHeader(): void { + const expanded = !this.headerVisible || this.isExpanded(); + + this.header.style.lineHeight = `${this.headerSize}px`; + 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 && 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; + protected abstract renderBody(container: HTMLElement): void; + protected abstract layoutBody(height: number, width: number): void; +} + +interface IDndContext { + draggable: PaneDraggable | null; +} + +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 _onDidDrop = this._register(new Emitter<{ from: Pane, to: Pane }>()); + readonly onDidDrop = this._onDidDrop.event; + + constructor(private pane: Pane, private dnd: IPaneDndController, private context: IDndContext) { + super(); + + pane.draggableElement.draggable = true; + this._register(domEvent(pane.draggableElement, 'dragstart')(this.onDragStart, this)); + this._register(domEvent(pane.dropTargetElement, 'dragenter')(this.onDragEnter, this)); + this._register(domEvent(pane.dropTargetElement, 'dragleave')(this.onDragLeave, this)); + this._register(domEvent(pane.dropTargetElement, 'dragend')(this.onDragEnd, this)); + this._register(domEvent(pane.dropTargetElement, 'drop')(this.onDrop, this)); + } + + private onDragStart(e: DragEvent): void { + if (!this.dnd.canDrag(this.pane) || !e.dataTransfer) { + e.preventDefault(); + e.stopPropagation(); + return; + } + + e.dataTransfer.effectAllowed = 'move'; + + if (isFirefox) { + // Firefox: requires to set a text data transfer to get going + e.dataTransfer?.setData(DataTransfers.TEXT, this.pane.draggableElement.textContent || ''); + } + + const dragImage = append(document.body, $('.monaco-drag-image', {}, this.pane.draggableElement.textContent || '')); + e.dataTransfer.setDragImage(dragImage, -10, -10); + setTimeout(() => document.body.removeChild(dragImage), 0); + + this.context.draggable = this; + } + + private onDragEnter(e: DragEvent): void { + if (!this.context.draggable || this.context.draggable === this) { + return; + } + + if (!this.dnd.canDrop(this.context.draggable.pane, this.pane)) { + return; + } + + this.dragOverCounter++; + this.render(); + } + + private onDragLeave(e: DragEvent): void { + if (!this.context.draggable || this.context.draggable === this) { + return; + } + + if (!this.dnd.canDrop(this.context.draggable.pane, this.pane)) { + return; + } + + this.dragOverCounter--; + + if (this.dragOverCounter === 0) { + this.render(); + } + } + + private onDragEnd(e: DragEvent): void { + if (!this.context.draggable) { + return; + } + + this.dragOverCounter = 0; + this.render(); + this.context.draggable = null; + } + + private onDrop(e: DragEvent): void { + if (!this.context.draggable) { + return; + } + + EventHelper.stop(e); + + this.dragOverCounter = 0; + this.render(); + + if (this.dnd.canDrop(this.context.draggable.pane, this.pane) && this.context.draggable !== this) { + this._onDidDrop.fire({ from: this.context.draggable.pane, to: this.pane }); + } + + this.context.draggable = null; + } + + private render(): void { + let backgroundColor: string | null = null; + + if (this.dragOverCounter > 0) { + backgroundColor = (this.pane.dropBackground || PaneDraggable.DefaultDragOverBackgroundColor).toString(); + } + + this.pane.dropTargetElement.style.backgroundColor = backgroundColor || ''; + } +} + +export interface IPaneDndController { + canDrag(pane: Pane): boolean; + canDrop(pane: Pane, overPane: Pane): boolean; +} + +export class DefaultPaneDndController implements IPaneDndController { + + canDrag(pane: Pane): boolean { + return true; + } + + canDrop(pane: Pane, overPane: Pane): boolean { + return true; + } +} + +export interface IPaneViewOptions { + dnd?: IPaneDndController; + orientation?: Orientation; +} + +interface IPaneItem { + pane: Pane; + disposable: IDisposable; +} + +export class PaneView extends Disposable { + + private dnd: IPaneDndController | undefined; + private dndContext: IDndContext = { draggable: null }; + private el: HTMLElement; + private paneItems: IPaneItem[] = []; + 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, { orientation: this.orientation })); + this.onDidSashChange = this.splitview.onDidSashChange; + } + + addPane(pane: Pane, size: number, index = this.splitview.length): void { + const disposables = new DisposableStore(); + pane.onDidChangeExpansionState(this.setupAnimation, this, disposables); + + const paneItem = { pane: pane, disposable: disposables }; + this.paneItems.splice(index, 0, paneItem); + pane.orientation = this.orientation; + pane.orthogonalSize = this.orthogonalSize; + this.splitview.addView(pane, size, index); + + if (this.dnd) { + const draggable = new PaneDraggable(pane, this.dnd, this.dndContext); + disposables.add(draggable); + disposables.add(draggable.onDidDrop(this._onDidDrop.fire, this._onDidDrop)); + } + } + + removePane(pane: Pane): void { + const index = this.paneItems.findIndex(item => item.pane === pane); + + if (index === -1) { + return; + } + + this.splitview.removeView(index); + const paneItem = this.paneItems.splice(index, 1)[0]; + paneItem.disposable.dispose(); + } + + movePane(from: Pane, to: Pane): void { + const fromIndex = this.paneItems.findIndex(item => item.pane === from); + const toIndex = this.paneItems.findIndex(item => item.pane === to); + + if (fromIndex === -1 || toIndex === -1) { + return; + } + + const [paneItem] = this.paneItems.splice(fromIndex, 1); + this.paneItems.splice(toIndex, 0, paneItem); + + this.splitview.moveView(fromIndex, toIndex); + } + + resizePane(pane: Pane, size: number): void { + const index = this.paneItems.findIndex(item => item.pane === pane); + + if (index === -1) { + return; + } + + this.splitview.resizeView(index, size); + } + + getPaneSize(pane: Pane): number { + const index = this.paneItems.findIndex(item => item.pane === pane); + + if (index === -1) { + return -1; + } + + return this.splitview.getViewSize(index); + } + + layout(height: number, width: number): void { + this.orthogonalSize = this.orientation === Orientation.VERTICAL ? width : height; + this.size = this.orientation === Orientation.HORIZONTAL ? width : height; + + for (const paneItem of this.paneItems) { + paneItem.pane.orthogonalSize = this.orthogonalSize; + } + + 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 { + if (typeof this.animationTimer === 'number') { + window.clearTimeout(this.animationTimer); + } + + this.el.classList.add('animated'); + + this.animationTimer = window.setTimeout(() => { + this.animationTimer = undefined; + this.el.classList.remove('animated'); + }, 200); + } + + dispose(): void { + super.dispose(); + + this.paneItems.forEach(i => i.disposable.dispose()); + } +} diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index 8b9333782667b..10c15797c4a9d 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -7,12 +7,12 @@ 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 } from 'vs/base/browser/dom'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; export interface ISplitViewStyles { @@ -23,14 +23,14 @@ const defaultStyles: ISplitViewStyles = { separatorBorder: Color.transparent }; -export interface ISplitViewOptions { +export interface ISplitViewOptions { readonly orientation?: Orientation; // default Orientation.VERTICAL readonly styles?: ISplitViewStyles; readonly orthogonalStartSash?: Sash; readonly orthogonalEndSash?: Sash; readonly inverseAltBehavior?: boolean; readonly proportionalLayout?: boolean; // default true, - readonly descriptor?: ISplitViewDescriptor; + readonly descriptor?: ISplitViewDescriptor; } /** @@ -42,14 +42,14 @@ export const enum LayoutPriority { High } -export interface IView { +export interface IView { readonly element: HTMLElement; readonly minimumSize: number; readonly maximumSize: number; readonly onDidChange: Event; readonly priority?: LayoutPriority; readonly snap?: boolean; - layout(size: number, orthogonalSize: number | undefined): void; + layout(size: number, offset: number, context: TLayoutContext | undefined): void; setVisible?(visible: boolean): void; } @@ -62,7 +62,7 @@ interface ISashEvent { type ViewItemSize = number | { cachedVisibleSize: number }; -abstract class ViewItem { +abstract class ViewItem { private _size: number; set size(size: number) { @@ -93,7 +93,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); @@ -109,47 +109,51 @@ abstract class ViewItem { get priority(): LayoutPriority | undefined { return this.view.priority; } get snap(): boolean { return !!this.view.snap; } + set enabled(enabled: boolean) { + this.container.style.pointerEvents = enabled ? '' : 'none'; + } + constructor( protected container: HTMLElement, - private view: IView, + private view: IView, size: ViewItemSize, private disposable: IDisposable ) { 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; } } - layout(position: number, orthogonalSize: number | undefined): void { - this.layoutContainer(position); - this.view.layout(this.size, orthogonalSize); + layout(offset: number, layoutContext: TLayoutContext | undefined): void { + this.layoutContainer(offset); + this.view.layout(this.size, offset, layoutContext); } - abstract layoutContainer(position: number): void; + abstract layoutContainer(offset: number): void; - dispose(): IView { + dispose(): IView { this.disposable.dispose(); return this.view; } } -class VerticalViewItem extends ViewItem { +class VerticalViewItem extends ViewItem { - layoutContainer(position: number): void { - this.container.style.top = `${position}px`; + layoutContainer(offset: number): void { + this.container.style.top = `${offset}px`; this.container.style.height = `${this.size}px`; } } -class HorizontalViewItem extends ViewItem { +class HorizontalViewItem extends ViewItem { - layoutContainer(position: number): void { - this.container.style.left = `${position}px`; + layoutContainer(offset: number): void { + this.container.style.left = `${offset}px`; this.container.style.width = `${this.size}px`; } } @@ -194,26 +198,26 @@ export namespace Sizing { export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; } } -export interface ISplitViewDescriptor { +export interface ISplitViewDescriptor { size: number; views: { visible?: boolean; size: number; - view: IView; + view: IView; }[]; } -export class SplitView extends Disposable { +export class SplitView extends Disposable { readonly orientation: Orientation; readonly el: HTMLElement; private sashContainer: HTMLElement; private viewContainer: HTMLElement; private size = 0; - private orthogonalSize: number | undefined; + private layoutContext: TLayoutContext | undefined; private contentSize = 0; private proportions: undefined | number[] = undefined; - private viewItems: ViewItem[] = []; + private viewItems: ViewItem[] = []; private sashItems: ISashItem[] = []; private sashDragState: ISashDragState | undefined; private state: State = State.Idle; @@ -262,7 +266,29 @@ export class SplitView extends Disposable { return this.sashItems.map(s => s.sash); } - constructor(container: HTMLElement, options: ISplitViewOptions = {}) { + private _startSnappingEnabled = true; + get startSnappingEnabled(): boolean { return this._startSnappingEnabled; } + set startSnappingEnabled(startSnappingEnabled: boolean) { + if (this._startSnappingEnabled === startSnappingEnabled) { + return; + } + + this._startSnappingEnabled = startSnappingEnabled; + this.updateSashEnablement(); + } + + private _endSnappingEnabled = true; + get endSnappingEnabled(): boolean { return this._endSnappingEnabled; } + set endSnappingEnabled(endSnappingEnabled: boolean) { + if (this._endSnappingEnabled === endSnappingEnabled) { + return; + } + + this._endSnappingEnabled = endSnappingEnabled; + this.updateSashEnablement(); + } + + constructor(container: HTMLElement, options: ISplitViewOptions = {}) { super(); this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation; @@ -270,12 +296,12 @@ 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 = append(this.el, $('.split-view-container')); this.style(options.styles || defaultStyles); @@ -297,19 +323,19 @@ 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 { + removeView(index: number, sizing?: Sizing): IView { if (this.state !== State.Idle) { throw new Error('Cant modify splitview'); } @@ -401,10 +427,10 @@ export class SplitView extends Disposable { return viewItem.cachedVisibleSize; } - layout(size: number, orthogonalSize?: number): void { + layout(size: number, layoutContext?: TLayoutContext): void { const previousSize = Math.max(this.size, this.contentSize); this.size = size; - this.orthogonalSize = orthogonalSize; + this.layoutContext = layoutContext; if (!this.proportions) { const indexes = range(this.viewItems.length); @@ -430,7 +456,11 @@ export class SplitView extends Disposable { } private onSashStart({ sash, start, alt }: ISashEvent): void { - const index = firstIndex(this.sashItems, item => item.sash === sash); + for (const item of this.viewItems) { + item.enabled = false; + } + + 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( @@ -535,9 +565,13 @@ export class SplitView extends Disposable { this._onDidSashChange.fire(index); this.sashDragState!.disposable.dispose(); this.saveProportions(); + + for (const item of this.viewItems) { + item.enabled = true; + } } - private onViewChange(item: ViewItem, size: number | undefined): void { + private onViewChange(item: ViewItem, size: number | undefined): void { const index = this.viewItems.indexOf(item); if (index < 0 || index >= this.viewItems.length) { @@ -584,7 +618,7 @@ export class SplitView extends Disposable { } distributeViewSizes(): void { - const flexibleViewItems: ViewItem[] = []; + const flexibleViewItems: ViewItem[] = []; let flexibleSize = 0; for (const item of this.viewItems) { @@ -615,7 +649,7 @@ export class SplitView extends Disposable { return this.viewItems[index].size; } - private doAddView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void { + private doAddView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void { if (this.state !== State.Idle) { throw new Error('Cant modify splitview'); } @@ -623,7 +657,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); @@ -655,13 +689,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 }) @@ -671,11 +709,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); @@ -849,17 +887,19 @@ export class SplitView extends Disposable { this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); // Layout views - let position = 0; + let offset = 0; for (const viewItem of this.viewItems) { - viewItem.layout(position, this.orthogonalSize); - position += viewItem.size; + viewItem.layout(offset, this.layoutContext); + offset += viewItem.size; } // Layout sashes this.sashItems.forEach(item => item.sash.layout()); + this.updateSashEnablement(); + } - // Update sashes enablement + private updateSashEnablement(): void { let previous = false; const collapsesDown = this.viewItems.map(i => previous = (i.size - i.minimumSize > 0) || previous); @@ -873,7 +913,12 @@ export class SplitView extends Disposable { previous = false; const expandsUp = reverseViews.map(i => previous = (i.maximumSize - i.size > 0) || previous).reverse(); - this.sashItems.forEach(({ sash }, index) => { + let position = 0; + for (let index = 0; index < this.sashItems.length; index++) { + const { sash } = this.sashItems[index]; + const viewItem = this.viewItems[index]; + position += viewItem.size; + const min = !(collapsesDown[index] && expandsUp[index + 1]); const max = !(expandsDown[index] && collapsesUp[index + 1]); @@ -886,9 +931,9 @@ export class SplitView extends Disposable { const snappedBefore = typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible; const snappedAfter = typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible; - if (snappedBefore && collapsesUp[index]) { + if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) { sash.state = SashState.Minimum; - } else if (snappedAfter && collapsesDown[index]) { + } else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) { sash.state = SashState.Maximum; } else { sash.state = SashState.Disabled; @@ -900,8 +945,7 @@ export class SplitView extends Disposable { } else { sash.state = SashState.Enabled; } - // } - }); + } } private getSashPosition(sash: Sash): number { @@ -911,7 +955,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 3e13d9ce66d0d..d2b361446a7d2 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: Action) => { - - // Return special action item for the toggle menu action + actionViewItemProvider: (action: IAction) => { 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.setActionContext(this.actionBar.context); + this.disposables.add(this._onDidChangeDropdownVisibility.add(this.toggleMenuActionViewItem.onDidChangeVisibility)); + + return this.toggleMenuActionViewItem; + } + + 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 + } ); - this.toggleMenuActionViewItem.value.setActionContext(this.actionBar.context); + result.setActionContext(this.actionBar.context); + this.submenuActionViewItems.push(result); + this.disposables.add(this._onDidChangeDropdownVisibility.add(result.onDidChangeVisibility)); - return this.toggleMenuActionViewItem.value; + return result; } - return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined; + 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 () => { - - // 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) }); - } + private clear(): void { + this.submenuActionViewItems = []; + this.disposables.clear(); + this.actionBar.clear(); + } - // 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 f47c7cf804f52..9201e6688328d 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,15 +22,29 @@ 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 { + + set context(context: TContext | undefined) { + this.data.context = context; + } + + get context(): TContext | undefined { + return this.data.context; + } + + constructor(private data: ElementsDragAndDropData, TContext>) { + super(data.elements.map(node => node.element)); + } +} function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { - const nodes = (data as ElementsDragAndDropData>).elements; - return new ElementsDragAndDropData(nodes.map(node => node.element)); + return new TreeElementsDragAndDropData(data); } return data; @@ -47,9 +61,9 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< return this.dnd.getDragURI(node.element); } - getDragLabel(nodes: ITreeNode[]): string | undefined { + getDragLabel(nodes: ITreeNode[], originalEvent: DragEvent): string | undefined { if (this.dnd.getDragLabel) { - return this.dnd.getDragLabel(nodes.map(node => node.element)); + return this.dnd.getDragLabel(nodes.map(node => node.element), originalEvent); } return undefined; @@ -87,7 +101,7 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< }, 500); } - if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined') { + if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined' || result.feedback) { if (!raw) { const accept = typeof result === 'boolean' ? result : result.accept; const effect = typeof result === 'boolean' ? undefined : result.effect; @@ -121,6 +135,12 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< this.dnd.drop(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent); } + + onDragEnd(originalEvent: DragEvent): void { + if (this.dnd.onDragEnd) { + this.dnd.onDragEnd(originalEvent); + } + } } function asListOptions(modelProvider: () => ITreeModel, options?: IAbstractTreeOptions): IListOptions> | undefined { @@ -141,12 +161,37 @@ 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); }, + getWidgetAriaLabel() { + return options.accessibilityProvider!.getWidgetAriaLabel(); + }, + getWidgetRole: options.accessibilityProvider && options.accessibilityProvider.getWidgetRole ? () => options.accessibilityProvider!.getWidgetRole!() : () => 'tree', getAriaLevel(node) { return node.depth; - } + }, + getActiveDescendantId: options.accessibilityProvider.getActiveDescendantId && (node => { + return options.accessibilityProvider!.getActiveDescendantId!(node.element); + }) }, keyboardNavigationLabelProvider: options.keyboardNavigationLabelProvider && { ...options.keyboardNavigationLabelProvider, @@ -154,20 +199,7 @@ function asListOptions(modelProvider: () => ITreeModel implements IListRenderer } const indent = TreeRenderer.DefaultIndent + (node.depth - 1) * this.indent; - templateData.twistie.style.marginLeft = `${indent}px`; + templateData.twistie.style.paddingLeft = `${indent}px`; templateData.indent.style.width = `${indent + this.indent - 16}px`; this.renderTwistie(node, templateData); @@ -373,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) { @@ -411,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) { @@ -454,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')); } }); @@ -618,10 +650,10 @@ class TypeFilterController implements IDisposable { 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')); + this.clearDomNode = append(controls, $('button.clear' + treeFilterClearIcon.cssSelector)); this.clearDomNode.tabIndex = -1; this.clearDomNode.title = localize('clear', "Clear"); @@ -674,7 +706,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; @@ -801,10 +833,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); @@ -827,13 +859,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"); } } @@ -845,11 +881,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; @@ -881,17 +917,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; @@ -924,7 +949,9 @@ 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; } export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { @@ -1004,7 +1031,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; } @@ -1053,26 +1080,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; @@ -1083,7 +1108,11 @@ class TreeNodeListMouseController extends MouseController< } if (expandOnlyOnTwistieClick && !onTwistie) { - return super.onPointer(e); + return super.onViewPointer(e); + } + + if (this.tree.expandOnlyOnDoubleClick && e.browserEvent.detail !== 2 && !onTwistie) { + return super.onViewPointer(e); } if (node.collapsible) { @@ -1097,11 +1126,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; @@ -1200,12 +1229,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; } @@ -1223,7 +1252,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>(); @@ -1274,7 +1303,7 @@ export abstract class AbstractTree implements IDisposable onDidModelSplice(() => null, null, this.disposables); // Active nodes can change when the model changes or when focus or selection change. - // We debouce it with 0 delay since these events may fire in the same stack and we only + // We debounce it with 0 delay since these events may fire in the same stack and we only // want to run this once. It also doesn't matter if it runs on the next tick since it's only // a nice to have UI feature. onDidChangeActiveNodes.input = Event.chain(Event.any(onDidModelSplice, this.focus.onDidChange, this.selection.onDidChange)) @@ -1290,7 +1319,7 @@ export abstract class AbstractTree implements IDisposable set.add(node); } - return fromSet(set); + return [...set.values()]; }).event; if (_options.keyboardSupport !== false) { @@ -1311,7 +1340,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 { @@ -1323,7 +1352,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) { @@ -1332,7 +1363,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 { @@ -1397,8 +1428,13 @@ export abstract class AbstractTree implements IDisposable return this.view.renderHeight; } - get firstVisibleElement(): T { + get firstVisibleElement(): T | undefined { const index = this.view.firstVisibleIndex; + + if (index < 0 || index >= this.view.length) { + return undefined; + } + const node = this.view.element(index); return node.element; } @@ -1409,6 +1445,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(); } @@ -1430,11 +1474,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); } @@ -1549,11 +1589,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 50ab5ce9be3c3..970eddf5c850c 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -6,19 +6,20 @@ import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; import { ObjectTree, IObjectTreeOptions, CompressibleObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; -import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper } from 'vs/base/browser/ui/tree/tree'; +import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop, TreeError, WeakMapper, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; 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; @@ -107,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; } @@ -126,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, @@ -141,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, @@ -149,10 +154,24 @@ function asTreeContextMenuEvent(e: ITreeContextMenuEvent extends ElementsDragAndDropData { + + set context(context: TContext | undefined) { + this.data.context = context; + } + + get context(): TContext | undefined { + return this.data.context; + } + + constructor(private data: ElementsDragAndDropData, TContext>) { + super(data.elements.map(node => node.element as T)); + } +} + function asAsyncDataTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { - const nodes = (data as ElementsDragAndDropData>).elements; - return new ElementsDragAndDropData(nodes.map(node => node.element)); + return new AsyncDataTreeElementsDragAndDropData(data); } return data; @@ -166,9 +185,9 @@ class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop[]): string | undefined { + getDragLabel(nodes: IAsyncDataTreeNode[], originalEvent: DragEvent): string | undefined { if (this.dnd.getDragLabel) { - return this.dnd.getDragLabel(nodes.map(node => node.element as T)); + return this.dnd.getDragLabel(nodes.map(node => node.element as T), originalEvent); } return undefined; @@ -187,6 +206,12 @@ class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { this.dnd.drop(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent); } + + onDragEnd(originalEvent: DragEvent): void { + if (this.dnd.onDragEnd) { + this.dnd.onDragEnd(originalEvent); + } + } } function asObjectTreeOptions(options?: IAsyncDataTreeOptions): IObjectTreeOptions, TFilterData> | undefined { @@ -208,9 +233,28 @@ 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); + }), + getActiveDescendantId: options.accessibilityProvider.getActiveDescendantId && (node => { + return options.accessibilityProvider!.getActiveDescendantId!(node.element as T); + }) }, filter: options.filter && { filter(e, parentVisibility) { @@ -229,7 +273,6 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt e => (options.expandOnlyOnTwistieClick as ((e: T) => boolean))(e.element as T) ) ), - ariaProvider: undefined, additionalScrollHeight: options.additionalScrollHeight }; } @@ -261,16 +304,16 @@ function dfs(node: IAsyncDataTreeNode, fn: (node: IAsyncDa node.children.forEach(child => dfs(child, fn)); } -export class AsyncDataTree implements IDisposable { +export class AsyncDataTree implements IDisposable, IThemable { protected readonly tree: ObjectTree, TFilterData>; - private readonly root: IAsyncDataTreeNode; + protected readonly root: IAsyncDataTreeNode; private readonly nodes = new Map>(); private readonly sorter?: ITreeSorter; 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; @@ -286,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; } @@ -300,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; @@ -363,6 +406,10 @@ export class AsyncDataTree implements IDisposable this.tree.updateOptions(options); } + get options(): IAsyncDataTreeOptions { + return this.tree.options as IAsyncDataTreeOptions; + } + // Widget getHTMLElement(): HTMLElement { @@ -401,14 +448,18 @@ export class AsyncDataTree implements IDisposable return this.tree.renderHeight; } - get firstVisibleElement(): T { - return this.tree.firstVisibleElement!.element as T; - } - get lastVisibleElement(): T { 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(); } @@ -435,7 +486,7 @@ export class AsyncDataTree implements IDisposable const viewStateContext = viewState && { viewState, focus: [], selection: [] } as IAsyncDataTreeViewStateContext; - await this._updateChildren(input, true, viewStateContext); + await this._updateChildren(input, true, false, viewStateContext); if (viewStateContext) { this.tree.setFocus(viewStateContext.focus); @@ -447,11 +498,11 @@ export class AsyncDataTree implements IDisposable } } - async updateChildren(element: TInput | T = this.root.element, recursive = true): Promise { - await this._updateChildren(element, recursive); + async updateChildren(element: TInput | T = this.root.element, recursive = true, rerender = false): Promise { + await this._updateChildren(element, recursive, rerender); } - private async _updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + private async _updateChildren(element: TInput | T = this.root.element, recursive = true, rerender = false, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { if (typeof this.root.element === 'undefined') { throw new TreeError(this.user, 'Tree input not set'); } @@ -461,7 +512,17 @@ export class AsyncDataTree implements IDisposable await Event.toPromise(this._onDidRender.event); } - await this.refreshAndRenderNode(this.getDataNode(element), recursive, viewStateContext); + const node = this.getDataNode(element); + await this.refreshAndRenderNode(node, recursive, viewStateContext); + + if (rerender) { + try { + this.tree.rerender(node); + } catch { + // missing nodes are fine, this could've resulted from + // parallel refresh calls, removing `node` altogether + } + } } resort(element: TInput | T = this.root.element, recursive = true): void { @@ -514,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); @@ -605,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); } @@ -687,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); @@ -724,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) { @@ -733,20 +793,19 @@ export class AsyncDataTree implements IDisposable result = createCancelablePromise(async () => { const children = await this.dataSource.getChildren(node.element!); - - if (this.sorter) { - children.sort(this.sorter.compare.bind(this.sorter)); - } - - return children; + return this.processChildren(children); }); 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); @@ -757,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 []; @@ -840,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)); } @@ -859,7 +920,7 @@ export class AsyncDataTree implements IDisposable return childrenToRefresh; } - private render(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): void { + protected render(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): void { const children = node.children.map(node => this.asTreeElement(node, viewStateContext)); this.tree.setChildren(node === this.root ? null : node, children); @@ -891,12 +952,20 @@ 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: Iterable): Iterable { + if (this.sorter) { + children = [...children].sort(this.sorter.compare.bind(this.sorter)); + } + + return children; + } + // view state getViewState(): IAsyncDataTreeViewState { @@ -982,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; } @@ -992,6 +1065,12 @@ class CompressibleAsyncDataTreeRenderer i } } + disposeCompressedElements(node: ITreeNode>, TFilterData>, index: number, templateData: IDataTreeListTemplateData, height: number | undefined): void { + if (this.renderer.disposeCompressedElements) { + this.renderer.disposeCompressedElements(this.compressibleNodeMapperProvider().map(node) as ITreeNode, TFilterData>, index, templateData.templateData, height); + } + } + disposeTemplate(templateData: IDataTreeListTemplateData): void { this.renderer.disposeTemplate(templateData.templateData); } @@ -1031,8 +1110,9 @@ export interface ICompressibleAsyncDataTreeOptionsUpdate extends IAsyncDataTreeO export class CompressibleAsyncDataTree extends AsyncDataTree { - protected readonly tree: CompressibleObjectTree, TFilterData>; + protected readonly tree!: CompressibleObjectTree, TFilterData>; protected readonly compressibleNodeMapper: CompressibleAsyncDataTreeNodeMapper = new WeakMapper(node => new CompressibleAsyncDataTreeNodeWrapper(node)); + private filter?: ITreeFilter; constructor( user: string, @@ -1044,6 +1124,7 @@ export class CompressibleAsyncDataTree extends As options: ICompressibleAsyncDataTreeOptions = {} ) { super(user, container, virtualDelegate, renderers, dataSource, options); + this.filter = options.filter; } protected createTree( @@ -1098,4 +1179,107 @@ export class CompressibleAsyncDataTree extends As return { focus, selection, expanded, scrollTop: this.scrollTop }; } + + protected render(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): void { + if (!this.identityProvider) { + return super.render(node, viewStateContext); + } + + // Preserve traits across compressions. Hacky but does the trick. + // This is hard to fix properly since it requires rewriting the traits + // across trees and lists. Let's just keep it this way for now. + const getId = (element: T) => this.identityProvider!.getId(element).toString(); + const getUncompressedIds = (nodes: IAsyncDataTreeNode[]): Set => { + const result = new Set(); + + for (const node of nodes) { + const compressedNode = this.tree.getCompressedTreeNode(node === this.root ? null : node); + + if (!compressedNode.element) { + continue; + } + + for (const node of compressedNode.element.elements) { + result.add(getId(node.element as T)); + } + } + + return result; + }; + + const oldSelection = getUncompressedIds(this.tree.getSelection() as IAsyncDataTreeNode[]); + const oldFocus = getUncompressedIds(this.tree.getFocus() as IAsyncDataTreeNode[]); + + super.render(node, viewStateContext); + + const selection = this.getSelection(); + let didChangeSelection = false; + + const focus = this.getFocus(); + let didChangeFocus = false; + + const visit = (node: ITreeNode> | null, TFilterData>) => { + const compressedNode = node.element; + + if (compressedNode) { + for (let i = 0; i < compressedNode.elements.length; i++) { + const id = getId(compressedNode.elements[i].element as T); + const element = compressedNode.elements[compressedNode.elements.length - 1].element as T; + + // github.com/microsoft/vscode/issues/85938 + if (oldSelection.has(id) && selection.indexOf(element) === -1) { + selection.push(element); + didChangeSelection = true; + } + + if (oldFocus.has(id) && focus.indexOf(element) === -1) { + focus.push(element); + didChangeFocus = true; + } + } + } + + node.children.forEach(visit); + }; + + visit(this.tree.getCompressedTreeNode(node === this.root ? null : node)); + + if (didChangeSelection) { + this.setSelection(selection); + } + + if (didChangeFocus) { + this.setFocus(focus); + } + } + + // 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: Iterable): Iterable { + if (this.filter) { + children = Iterable.filter(children, e => { + const result = this.filter!.filter(e, TreeVisibility.Visible); + const visibility = getVisibility(result); + + if (visibility === TreeVisibility.Recurse) { + throw new Error('Recursive tree visibility not supported in async data compressed trees'); + } + + return visibility === TreeVisibility.Visible; + }); + } + + return super.processChildren(children); + } +} + +function getVisibility(filterResult: TreeFilterResult): TreeVisibility { + if (typeof filterResult === 'boolean') { + return filterResult ? TreeVisibility.Visible : TreeVisibility.Hidden; + } else if (isFilterResult(filterResult)) { + return getVisibleState(filterResult.visibility); + } else { + return getVisibleState(filterResult); + } } diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index 4b55b9b6f775e..150f49ce53f42 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?: Iterator> | ICompressedTreeElement[]; + 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,11 +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; } @@ -156,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 @@ -177,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>) => { @@ -206,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); @@ -288,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(); } @@ -338,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); } }; } @@ -400,7 +415,7 @@ export class CompressibleObjectTreeModel, TFilterData constructor( user: string, - list: ISpliceable>, + list: IList>, options: ICompressibleObjectTreeModelOptions = {} ) { this.elementMapper = options.elementMapper || DefaultElementMapper; @@ -410,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); } @@ -422,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); } @@ -486,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 2dbfc990a6138..4e97f4ed9b6c2 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 7cf73ee5fd7b9..5272fa1971a81 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 c7f287e873ba0..362ef7e1a30a4 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 7dc1ebd8cf045..0000000000000 --- 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 c3633c0ddabee..0000000000000 --- 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 e762f06d5e6e8..0000000000000 --- 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/panelviewlet.css b/src/vs/base/browser/ui/tree/media/panelviewlet.css deleted file mode 100644 index 7aba33ef44025..0000000000000 --- a/src/vs/base/browser/ui/tree/media/panelviewlet.css +++ /dev/null @@ -1,12 +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-panel-view .panel > .panel-header h3.title { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - font-size: 11px; - margin: 0; -} diff --git a/src/vs/base/browser/ui/tree/media/paneviewlet.css b/src/vs/base/browser/ui/tree/media/paneviewlet.css new file mode 100644 index 0000000000000..eb1e0be70b292 --- /dev/null +++ b/src/vs/base/browser/ui/tree/media/paneviewlet.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-pane-view .pane > .pane-header h3.title { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + font-size: 11px; + margin: 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 c2c2298dd5c4d..0000000000000 --- 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 3732cbc04b8d9..0000000000000 --- 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 1952ad63f8481..0000000000000 --- 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 5570923e1753a..0000000000000 --- 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 b370009330c04..0000000000000 --- 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 939ebc8b96953..0000000000000 --- 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 49acc994b208f..06f775d624612 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -14,7 +14,7 @@ height: 100%; position: absolute; top: 0; - left: 18px; + left: 16px; pointer-events: none; } @@ -41,7 +41,7 @@ .monaco-tl-twistie { font-size: 10px; text-align: right; - margin-right: 6px; + padding-right: 6px; flex-shrink: 0; width: 16px; display: flex !important; @@ -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 757683c18aff3..60601a517755c 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 9d6b9749510e0..59afa8d4b9828 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 374296fa573e3..085bb3d90b742 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 e74f249c17db1..834ab449fdd66 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 0000000000000..bccd518892313 --- /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/browser/ui/widget.ts b/src/vs/base/browser/ui/widget.ts index 569ef4925fc45..7e44d9a5a4b96 100644 --- a/src/vs/base/browser/ui/widget.ts +++ b/src/vs/base/browser/ui/widget.ts @@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Disposable } from 'vs/base/common/lifecycle'; +import { Gesture } from 'vs/base/browser/touch'; export abstract class Widget extends Disposable { @@ -49,4 +50,8 @@ export abstract class Widget extends Disposable { protected onchange(domNode: HTMLElement, listener: (e: Event) => void): void { this._register(dom.addDisposableListener(domNode, dom.EventType.CHANGE, listener)); } + + protected ignoreGesture(domNode: HTMLElement): void { + Gesture.ignoreTarget(domNode); + } } diff --git a/src/vs/base/common/actions.ts b/src/vs/base/common/actions.ts index 135008aa81d17..196aeda394569 100644 --- a/src/vs/base/common/actions.ts +++ b/src/vs/base/common/actions.ts @@ -39,14 +39,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 +222,27 @@ 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 type SubmenuActions = IAction[] | (() => IAction[]); + +export class SubmenuAction extends Action { + + get actions(): IAction[] { + return Array.isArray(this._actions) ? this._actions : this._actions(); + } + + constructor(id: string, label: string, private _actions: SubmenuActions, cssClass?: string) { + super(id, label, cssClass, true); + } +} diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index 8aa520d20c176..ebea36794d0d7 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -6,14 +6,9 @@ import { URI } from 'vs/base/common/uri'; export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string { - return URI.parse(requirefn.toUrl(relativePath)).fsPath; + return getUriFromAmdModule(requirefn, relativePath).fsPath; } -/** - * 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); +export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI { + return URI.parse(requirefn.toUrl(relativePath)); } diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 8ddd98a5a82ca..a8160f25ce8c4 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -372,12 +372,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,22 +399,13 @@ 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; -} - +/** + * @deprecated ES6: use `Array.find` + */ 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); + const index = array.findIndex(fn); return index < 0 ? notFoundValue : array[index]; } @@ -471,20 +456,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 +472,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,17 +549,6 @@ 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) : @@ -584,3 +558,7 @@ export function mapArrayOrNot(items: T | T[], fn: (_: T) => U): U | U[] { 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 1e227df640eed..9e2510b4d0bc5 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 39055d0201e25..ea4ccc21a5d0f 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -34,7 +34,7 @@ export function createCancelablePromise(callback: (token: CancellationToken) }); }); - return new class implements CancelablePromise { + return >new class { cancel() { source.cancel(); } @@ -52,8 +52,22 @@ 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)))]); +} + +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 { @@ -156,6 +170,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. * @@ -184,13 +217,14 @@ export class Delayer implements IDisposable { private timeout: any; private completionPromise: Promise | null; private doResolve: ((value?: any | Promise) => void) | null; - private doReject?: (err: any) => void; + private doReject: ((err: any) => void) | null; private task: ITask> | null; constructor(public defaultDelay: number) { this.timeout = null; this.completionPromise = null; this.doResolve = null; + this.doReject = null; this.task = null; } @@ -205,16 +239,20 @@ export class Delayer implements IDisposable { }).then(() => { this.completionPromise = null; this.doResolve = null; - const task = this.task!; - this.task = null; - - return task(); + if (this.task) { + const task = this.task; + this.task = null; + return task(); + } + return undefined; }); } this.timeout = setTimeout(() => { this.timeout = null; - this.doResolve!(null); + if (this.doResolve) { + this.doResolve(null); + } }, delay); return this.completionPromise; @@ -228,7 +266,9 @@ export class Delayer implements IDisposable { this.cancelTimeout(); if (this.completionPromise) { - this.doReject!(errors.canceled()); + if (this.doReject) { + this.doReject(errors.canceled()); + } this.completionPromise = null; } } @@ -392,7 +432,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; } @@ -474,8 +514,9 @@ export class Queue extends Limiter { * A helper to organize queues per resource. The ResourceQueue makes sure to manage queues per resource * by disposing them once the queue is empty. */ -export class ResourceQueue { - private queues: Map> = new Map(); +export class ResourceQueue implements IDisposable { + + private readonly queues = new Map>(); queueFor(resource: URI): Queue { const key = resource.toString(); @@ -491,6 +532,11 @@ export class ResourceQueue { return this.queues.get(key)!; } + + dispose(): void { + this.queues.forEach(queue => queue.dispose()); + this.queues.clear(); + } } export class TimeoutTimer implements IDisposable { @@ -572,10 +618,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); } @@ -605,6 +651,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. */ @@ -661,7 +715,7 @@ export class RunOnceWorker extends RunOnceScheduler { export interface IdleDeadline { readonly didTimeout: boolean; - timeRemaining(): DOMHighResTimeStamp; + timeRemaining(): number; } /** * Execute the callback the next time the browser is idle @@ -737,7 +791,7 @@ export class IdleValue { this._handle.dispose(); } - getValue(): T { + get value(): T { if (!this._didRun) { this._handle.dispose(); this._executor(); @@ -766,3 +820,107 @@ 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 diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts index 25371f5ec795e..6c6285e0c3b6f 100644 --- a/src/vs/base/common/buffer.ts +++ b/src/vs/base/common/buffer.ts @@ -6,7 +6,7 @@ import * as strings from 'vs/base/common/strings'; import * as streams from 'vs/base/common/stream'; -declare var Buffer: any; +declare const Buffer: any; const hasBuffer = (typeof Buffer !== 'undefined'); const hasTextEncoder = (typeof TextEncoder !== 'undefined'); @@ -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/cache.ts b/src/vs/base/common/cache.ts index bc585927f03b8..3269508e02bbf 100644 --- a/src/vs/base/common/cache.ts +++ b/src/vs/base/common/cache.ts @@ -22,7 +22,6 @@ export class Cache { const cts = new CancellationTokenSource(); const promise = this.task(cts.token); - promise.finally(() => cts.dispose()); this.result = { promise, diff --git a/src/vs/base/common/cancellation.ts b/src/vs/base/common/cancellation.ts index f741f6b7af11d..cb8c8c6da2d8b 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 { + + /** + * A flag signalling is cancellation has been requested. + */ readonly isCancellationRequested: boolean; + /** - * An event emitted when cancellation is requested + * 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 3b8c64267106e..23b840b294200 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 511892797802f..5b3541125e08f 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 new file mode 100644 index 0000000000000..b72c1d621043c --- /dev/null +++ b/src/vs/base/common/codicons.ts @@ -0,0 +1,511 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * 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' }); +} + + + + +const escapeCodiconsRegex = /(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi; +export function escapeCodicons(text: string): string { + return text.replace(escapeCodiconsRegex, (match, escaped) => escaped ? match : `\\${match}`); +} + +const markdownEscapedCodiconsRegex = /\\\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi; +export function markdownEscapeEscapedCodicons(text: string): string { + // Need to add an extra \ for escaping in markdown + return text.replace(markdownEscapedCodiconsRegex, match => `\\${match}`); +} + +const markdownUnescapeCodiconsRegex = /(\\)?\$\\\(([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?)\\\)/gi; +export function markdownUnescapeCodicons(text: string): string { + return text.replace(markdownUnescapeCodiconsRegex, (match, escaped, codicon) => escaped ? match : `$(${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 f185d43965112..8729b7303aeb3 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 8068e76aadd38..58397cfbcaee5 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -399,6 +399,23 @@ export class Color { return new Color(new RGBA(r, g, b, a)); } + makeOpaque(opaqueBackground: Color): Color { + if (this.isOpaque() || opaqueBackground.rgba.a !== 1) { + // only allow to blend onto a non-opaque color onto a opaque color + return this; + } + + const { r, g, b, a } = this.rgba; + + // https://stackoverflow.com/questions/12228548/finding-equivalent-color-with-opacity + return new Color(new RGBA( + opaqueBackground.rgba.r - a * (opaqueBackground.rgba.r - r), + opaqueBackground.rgba.g - a * (opaqueBackground.rgba.g - g), + opaqueBackground.rgba.b - a * (opaqueBackground.rgba.b - b), + 1 + )); + } + flatten(...backgrounds: Color[]): Color { const background = backgrounds.reduceRight((accumulator, color) => { return Color._flatten(color, accumulator); diff --git a/src/vs/base/common/comparers.ts b/src/vs/base/common/comparers.ts index d6dbb1784f1ab..7b45ce5abb6dc 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 a6da9d5ae3363..443827d587bc1 100644 --- a/src/vs/base/common/date.ts +++ b/src/vs/base/common/date.ts @@ -4,6 +4,120 @@ *--------------------------------------------------------------------------------------------*/ 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() + diff --git a/src/vs/base/common/decorators.ts b/src/vs/base/common/decorators.ts index 17dfadbf784ea..f9e39b6b941d7 100644 --- a/src/vs/base/common/decorators.ts +++ b/src/vs/base/common/decorators.ts @@ -84,11 +84,11 @@ export function memoize(target: any, key: string, descriptor: any) { return createMemoizer()(target, key, descriptor); } -export interface IDebouceReducer { +export interface IDebounceReducer { (previousValue: T, ...args: any[]): T; } -export function debounce(delay: number, reducer?: IDebouceReducer, initialValueProvider?: () => T): Function { +export function debounce(delay: number, reducer?: IDebounceReducer, initialValueProvider?: () => T): Function { return createDecorator((fn, key) => { const timerKey = `$debounce$${key}`; const resultKey = `$debounce$result$${key}`; @@ -112,3 +112,44 @@ export function debounce(delay: number, reducer?: IDebouceReducer, initial }; }); } + +export function throttle(delay: number, reducer?: IDebounceReducer, initialValueProvider?: () => T): Function { + return createDecorator((fn, key) => { + const timerKey = `$throttle$timer$${key}`; + const resultKey = `$throttle$result$${key}`; + const lastRunKey = `$throttle$lastRun$${key}`; + const pendingKey = `$throttle$pending$${key}`; + + return function (this: any, ...args: any[]) { + if (!this[resultKey]) { + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; + } + if (this[lastRunKey] === null || this[lastRunKey] === undefined) { + this[lastRunKey] = -Number.MAX_VALUE; + } + + if (reducer) { + this[resultKey] = reducer(this[resultKey], ...args); + } + + if (this[pendingKey]) { + return; + } + + const nextTime = this[lastRunKey] + delay; + if (nextTime <= Date.now()) { + this[lastRunKey] = Date.now(); + fn.apply(this, [this[resultKey]]); + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; + } else { + this[pendingKey] = true; + this[timerKey] = setTimeout(() => { + this[pendingKey] = false; + this[lastRunKey] = Date.now(); + fn.apply(this, [this[resultKey]]); + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; + }, nextTime - Date.now()); + } + }; + }); +} diff --git a/src/vs/base/common/errorMessage.ts b/src/vs/base/common/errorMessage.ts index e3fc44bacd3e2..f686dd28bd773 100644 --- a/src/vs/base/common/errorMessage.ts +++ b/src/vs/base/common/errorMessage.ts @@ -8,15 +8,11 @@ import * as types from 'vs/base/common/types'; import * as arrays from 'vs/base/common/arrays'; function exceptionToErrorMessage(exception: any, verbose: boolean): string { - if (exception.message) { - if (verbose && (exception.stack || exception.stacktrace)) { - return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), stackToString(exception.stack) || stackToString(exception.stacktrace)); - } - - return detectSystemErrorMessage(exception); + if (verbose && (exception.stack || exception.stacktrace)) { + return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), stackToString(exception.stack) || stackToString(exception.stacktrace)); } - return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details."); + return detectSystemErrorMessage(exception); } function stackToString(stack: string[] | string | undefined): string | undefined { @@ -34,7 +30,7 @@ function detectSystemErrorMessage(exception: any): string { return nls.localize('nodeExceptionMessage', "A system error occurred ({0})", exception.message); } - return exception.message; + return exception.message || nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details."); } /** diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 0beea24d6f3e9..f6d4f5cf89386 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -31,7 +31,7 @@ export class ErrorHandler { }; } - public addListener(listener: ErrorListenerCallback): ErrorListenerUnbind { + addListener(listener: ErrorListenerCallback): ErrorListenerUnbind { this.listeners.push(listener); return () => { @@ -49,21 +49,21 @@ export class ErrorHandler { this.listeners.splice(this.listeners.indexOf(listener), 1); } - public setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void { + setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void { this.unexpectedErrorHandler = newUnexpectedErrorHandler; } - public getUnexpectedErrorHandler(): (e: any) => void { + getUnexpectedErrorHandler(): (e: any) => void { return this.unexpectedErrorHandler; } - public onUnexpectedError(e: any): void { + onUnexpectedError(e: any): void { this.unexpectedErrorHandler(e); this.emit(e); } // For external errors, we don't want the listeners to be called - public onUnexpectedExternalError(e: any): void { + onUnexpectedExternalError(e: any): void { this.unexpectedErrorHandler(e); } } @@ -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 133febb84536f..fa92b7f45266f 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 b85073b6786e1..f70aa9d8d2f31 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -7,6 +7,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { once as onceFn } from 'vs/base/common/functional'; import { Disposable, IDisposable, toDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; +import { CancellationToken } from 'vs/base/common/cancellation'; /** * To an event a function with one or zero parameters @@ -84,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))); } @@ -147,6 +150,7 @@ export namespace Event { if (leading && !handle) { emitter.fire(output); + output = undefined; } clearTimeout(handle); @@ -269,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; @@ -289,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)); } @@ -321,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 { @@ -433,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!); @@ -569,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) { @@ -653,27 +660,39 @@ export interface IWaitUntil { export class AsyncEmitter extends Emitter { - private _asyncDeliveryQueue?: [Listener, T, Promise[]][]; + private _asyncDeliveryQueue?: LinkedList<[Listener, Omit]>; - async fireAsync(eventFn: (thenables: Promise[], listener: Function) => T): Promise { + async fireAsync(data: Omit, token: CancellationToken, promiseJoin?: (p: Promise, listener: Function) => Promise): Promise { if (!this._listeners) { return; } - // put all [listener,event]-pairs into delivery queue - // then emit all event. an inner/nested event might be - // the driver of this if (!this._asyncDeliveryQueue) { - this._asyncDeliveryQueue = []; + this._asyncDeliveryQueue = new LinkedList(); } - for (let iter = this._listeners.iterator(), e = iter.next(); !e.done; e = iter.next()) { - const thenables: Promise[] = []; - this._asyncDeliveryQueue.push([e.value, eventFn(thenables, typeof e.value === 'function' ? e.value : e.value[0]), thenables]); + for (const listener of this._listeners) { + this._asyncDeliveryQueue.push([listener, data]); } - while (this._asyncDeliveryQueue.length > 0) { - const [listener, event, thenables] = this._asyncDeliveryQueue.shift()!; + while (this._asyncDeliveryQueue.size > 0 && !token.isCancellationRequested) { + + const [listener, data] = this._asyncDeliveryQueue.shift()!; + const thenables: Promise[] = []; + + const event = { + ...data, + waitUntil: (p: Promise): void => { + if (Object.isFrozen(thenables)) { + throw new Error('waitUntil can NOT be called asynchronous'); + } + if (promiseJoin) { + p = promiseJoin(p, typeof listener === 'function' ? listener : listener[0]); + } + thenables.push(p); + } + }; + try { if (typeof listener === 'function') { listener.call(undefined, event); diff --git a/src/vs/base/common/extpath.ts b/src/vs/base/common/extpath.ts index 907dc51ff9333..05d343a5e1b42 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; @@ -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 5e3150d33080a..adff05173563b 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -543,7 +543,7 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu const patternLen = pattern.length > _maxLen ? _maxLen : pattern.length; const wordLen = word.length > _maxLen ? _maxLen : word.length; - if (patternStart >= patternLen || wordStart >= wordLen || patternLen > wordLen) { + if (patternStart >= patternLen || wordStart >= wordLen || (patternLen - patternStart) > (wordLen - wordStart)) { return undefined; } @@ -559,13 +559,19 @@ 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++) { + for (row = 1, patternPos = patternStart; patternPos < patternLen; row++, patternPos++) { - for (column = 1, wordPos = wordStart; wordPos < wordLen; column++ , wordPos++) { + for (column = 1, wordPos = wordStart; wordPos < wordLen; column++, wordPos++) { 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 4587a5b75421e..b437cc98c4608 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/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts new file mode 100644 index 0000000000000..11fc4bc0ed3e5 --- /dev/null +++ b/src/vs/base/common/fuzzyScorer.ts @@ -0,0 +1,882 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { compareAnything } from 'vs/base/common/comparers'; +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'; + +//#region Fuzzy scorer + +export type FuzzyScore = [number /* score */, number[] /* match positions */]; +export type FuzzyScorerCache = { [key: string]: IItemScore }; + +const NO_MATCH = 0; +const NO_SCORE: FuzzyScore = [NO_MATCH, []]; + +// const DEBUG = false; +// const DEBUG_MATRIX = false; + +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 + } + + const targetLength = target.length; + const queryLength = query.length; + + if (targetLength < queryLength) { + return NO_SCORE; // impossible for query to be contained in target + } + + // if (DEBUG) { + // console.group(`Target: ${target}, Query: ${query}`); + // } + + const targetLower = target.toLowerCase(); + + // When not searching fuzzy, we require the query to be contained fully + // in the target string contiguously. + if (!fuzzy) { + if (!targetLower.includes(queryLower)) { + // if (DEBUG) { + // console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`); + // } + + return NO_SCORE; + } + } + + const res = doScoreFuzzy(query, queryLower, queryLength, target, targetLower, targetLength); + + // if (DEBUG) { + // console.log(`%cFinal Score: ${res[0]}`, 'font-weight: bold'); + // console.groupEnd(); + // } + + return res; +} + +function doScoreFuzzy(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): FuzzyScore { + const scores: number[] = []; + const matches: number[] = []; + + // + // Build Scorer Matrix: + // + // The matrix is composed of query q and target t. For each index we score + // q[i] with t[i] and compare that with the previous score. If the score is + // equal or larger, we keep the match. In addition to the score, we also keep + // the length of the consecutive matches to use as boost for the score. + // + // t a r g e t + // q + // u + // e + // r + // y + // + for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) { + const queryIndexOffset = queryIndex * targetLength; + const queryIndexPreviousOffset = queryIndexOffset - targetLength; + + const queryIndexGtNull = queryIndex > 0; + + const queryCharAtIndex = query[queryIndex]; + const queryLowerCharAtIndex = queryLower[queryIndex]; + + for (let targetIndex = 0; targetIndex < targetLength; targetIndex++) { + const targetIndexGtNull = targetIndex > 0; + + const currentIndex = queryIndexOffset + targetIndex; + const leftIndex = currentIndex - 1; + const diagIndex = queryIndexPreviousOffset + targetIndex - 1; + + const leftScore = targetIndexGtNull ? scores[leftIndex] : 0; + const diagScore = queryIndexGtNull && targetIndexGtNull ? scores[diagIndex] : 0; + + const matchesSequenceLength = queryIndexGtNull && targetIndexGtNull ? matches[diagIndex] : 0; + + // If we are not matching on the first query character any more, we only produce a + // score if we had a score previously for the last query index (by looking at the diagScore). + // This makes sure that the query always matches in sequence on the target. For example + // given a target of "ede" and a query of "de", we would otherwise produce a wrong high score + // for query[1] ("e") matching on target[0] ("e") because of the "beginning of word" boost. + let score: number; + if (!diagScore && queryIndexGtNull) { + score = 0; + } else { + score = computeCharScore(queryCharAtIndex, queryLowerCharAtIndex, target, targetLower, targetIndex, matchesSequenceLength); + } + + // We have a score and its equal or larger than the left score + // Match: sequence continues growing from previous diag value + // Score: increases by diag score value + if (score && diagScore + score >= leftScore) { + matches[currentIndex] = matchesSequenceLength + 1; + scores[currentIndex] = diagScore + score; + } + + // We either have no score or the score is lower than the left score + // Match: reset to 0 + // Score: pick up from left hand side + else { + matches[currentIndex] = NO_MATCH; + scores[currentIndex] = leftScore; + } + } + } + + // Restore Positions (starting from bottom right of matrix) + const positions: number[] = []; + let queryIndex = queryLength - 1; + let targetIndex = targetLength - 1; + while (queryIndex >= 0 && targetIndex >= 0) { + const currentIndex = queryIndex * targetLength + targetIndex; + const match = matches[currentIndex]; + if (match === NO_MATCH) { + targetIndex--; // go left + } else { + positions.push(targetIndex); + + // go up and left + queryIndex--; + targetIndex--; + } + } + + // Print matrix + // if (DEBUG_MATRIX) { + // printMatrix(query, target, matches, scores); + // } + + return [scores[queryLength * targetLength - 1], positions.reverse()]; +} + +function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: string, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { + let score = 0; + + if (!considerAsEqual(queryLowerCharAtIndex, targetLower[targetIndex])) { + return score; // no match of characters + } + + // Character match bonus + score += 1; + + // if (DEBUG) { + // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLowerCharAtIndex} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal'); + // } + + // Consecutive match bonus + if (matchesSequenceLength > 0) { + score += (matchesSequenceLength * 5); + + // if (DEBUG) { + // console.log(`Consecutive match bonus: +${matchesSequenceLength * 5}`); + // } + } + + // Same case bonus + if (queryCharAtIndex === target[targetIndex]) { + score += 1; + + // if (DEBUG) { + // console.log('Same case bonus: +1'); + // } + } + + // Start of word bonus + if (targetIndex === 0) { + score += 8; + + // if (DEBUG) { + // console.log('Start of word bonus: +8'); + // } + } + + else { + + // After separator bonus + const separatorBonus = scoreSeparatorAtPos(target.charCodeAt(targetIndex - 1)); + if (separatorBonus) { + score += separatorBonus; + + // if (DEBUG) { + // console.log(`After separtor bonus: +${separatorBonus}`); + // } + } + + // Inside word upper case bonus (camel case) + else if (isUpper(target.charCodeAt(targetIndex))) { + score += 2; + + // if (DEBUG) { + // console.log('Inside word upper case bonus: +2'); + // } + } + } + + // if (DEBUG) { + // console.groupEnd(); + // } + + 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: + case CharCode.Backslash: + return 5; // prefer path separators... + case CharCode.Underline: + case CharCode.Dash: + case CharCode.Period: + case CharCode.Space: + case CharCode.SingleQuote: + case CharCode.DoubleQuote: + case CharCode.Colon: + return 4; // ...over other separators + default: + return 0; + } +} + +// function printMatrix(query: string, target: string, matches: number[], scores: number[]): void { +// console.log('\t' + target.split('').join('\t')); +// for (let queryIndex = 0; queryIndex < query.length; queryIndex++) { +// let line = query[queryIndex] + '\t'; +// for (let targetIndex = 0; targetIndex < target.length; targetIndex++) { +// const currentIndex = queryIndex * target.length + targetIndex; +// line = line + 'M' + matches[currentIndex] + '/' + 'S' + scores[currentIndex] + '\t'; +// } + +// console.log(line); +// } +// } + +//#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. + */ +export interface IItemScore { + + /** + * Overall score. + */ + score: number; + + /** + * Matches within the label. + */ + labelMatch?: IMatch[]; + + /** + * Matches within the description. + */ + descriptionMatch?: IMatch[]; +} + +const NO_ITEM_SCORE: IItemScore = Object.freeze({ score: 0 }); + +export interface IItemAccessor { + + /** + * Just the label of the item to score on. + */ + getItemLabel(item: T): string | undefined; + + /** + * The optional description of the item to score on. + */ + getItemDescription(item: T): string | undefined; + + /** + * 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_THRESHOLD = 1 << 17; +const LABEL_SCORE_THRESHOLD = 1 << 16; + +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 + } + + const label = accessor.getItemLabel(item); + if (!label) { + return NO_ITEM_SCORE; // we need a label at least + } + + 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.normalized}${Array.isArray(query.values) ? query.values.length : ''}${fuzzy}`; + } else { + cacheHash = `${label}${query.normalized}${Array.isArray(query.values) ? query.values.length : ''}${fuzzy}`; + } + + const cached = cache[cacheHash]; + if (cached) { + return cached; + } + + const itemScore = doScoreItemFuzzy(label, description, accessor.getItemPath(item), query, fuzzy); + cache[cacheHash] = itemScore; + + return itemScore; +} + +function doScoreItemFuzzy(label: string, description: string | undefined, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore { + const preferLabelMatches = !path || !query.containsPathSeparator; + + // 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 }; + } + + // Score: multiple inputs + if (query.values && query.values.length > 1) { + return doScoreItemFuzzyMultiple(label, description, path, query.values, preferLabelMatches, fuzzy); + } + + // 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; + } + + totalScore += score; + if (labelMatch) { + totalLabelMatches.push(...labelMatch); + } + + if (descriptionMatch) { + totalDescriptionMatches.push(...descriptionMatch); + } + } + + // 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) { + descriptionPrefix = `${description}${sep}`; // assume this is a file path + } + + const descriptionPrefixLength = descriptionPrefix.length; + const descriptionAndLabel = `${descriptionPrefix}${label}`; + + const [labelDescriptionScore, labelDescriptionPositions] = scoreFuzzy(descriptionAndLabel, query.normalized, query.normalizedLowercase, fuzzy); + if (labelDescriptionScore) { + const labelDescriptionMatches = createMatches(labelDescriptionPositions); + const labelMatch: IMatch[] = []; + const descriptionMatch: IMatch[] = []; + + // We have to split the matches back onto the label and description portions + labelDescriptionMatches.forEach(h => { + + // Match overlaps label and description part, we need to split it up + if (h.start < descriptionPrefixLength && h.end > descriptionPrefixLength) { + labelMatch.push({ start: 0, end: h.end - descriptionPrefixLength }); + descriptionMatch.push({ start: h.start, end: descriptionPrefixLength }); + } + + // Match on label part + else if (h.start >= descriptionPrefixLength) { + labelMatch.push({ start: h.start - descriptionPrefixLength, end: h.end - descriptionPrefixLength }); + } + + // Match on description part + else { + descriptionMatch.push(h); + } + }); + + return { score: labelDescriptionScore, labelMatch, descriptionMatch }; + } + } + + return NO_ITEM_SCORE; +} + +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.) 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.) matches on label are considered higher compared to label+description matches + if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) { + if (scoreA !== scoreB) { + return scoreA > scoreB ? -1 : 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; + } + } + + // 3.) compare by score in label+description + if (scoreA !== scoreB) { + return scoreA > scoreB ? -1 : 1; + } + + // 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; + } + + // 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 { + let matchStart: number = -1; + let matchEnd: number = -1; + + // If we have description matches, the start is first of description match + if (score.descriptionMatch && score.descriptionMatch.length) { + matchStart = score.descriptionMatch[0].start; + } + + // Otherwise, the start is the first label match + else if (score.labelMatch && score.labelMatch.length) { + matchStart = score.labelMatch[0].start; + } + + // If we have label match, the end is the last label match + // If we had a description match, we add the length of the description + // as offset to the end to indicate this. + if (score.labelMatch && score.labelMatch.length) { + matchEnd = score.labelMatch[score.labelMatch.length - 1].end; + if (score.descriptionMatch && score.descriptionMatch.length) { + const itemDescription = accessor.getItemDescription(item); + if (itemDescription) { + matchEnd += itemDescription.length; + } + } + } + + // If we have just a description match, the end is the last description match + else if (score.descriptionMatch && score.descriptionMatch.length) { + matchEnd = score.descriptionMatch[score.descriptionMatch.length - 1].end; + } + + return matchEnd - matchStart; +} + +function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number { + if ((!matchesA && !matchesB) || ((!matchesA || !matchesA.length) && (!matchesB || !matchesB.length))) { + return 0; // make sure to not cause bad comparing when matches are not provided + } + + if (!matchesB || !matchesB.length) { + return -1; + } + + if (!matchesA || !matchesA.length) { + return 1; + } + + // Compute match length of A (first to last match) + const matchStartA = matchesA[0].start; + const matchEndA = matchesA[matchesA.length - 1].end; + const matchLengthA = matchEndA - matchStartA; + + // Compute match length of B (first to last match) + const matchStartB = matchesB[0].start; + const matchEndB = matchesB[matchesB.length - 1].end; + const matchLengthB = matchEndB - matchStartB; + + // Prefer shorter match length + return matchLengthA === matchLengthB ? 0 : matchLengthB < matchLengthA ? 1 : -1; +} + +function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor): number { + + // check for label + description length and prefer shorter + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; + + const descriptionA = accessor.getItemDescription(itemA); + const descriptionB = accessor.getItemDescription(itemB); + + const labelDescriptionALength = labelA.length + (descriptionA ? descriptionA.length : 0); + const labelDescriptionBLength = labelB.length + (descriptionB ? descriptionB.length : 0); + + if (labelDescriptionALength !== labelDescriptionBLength) { + return labelDescriptionALength - labelDescriptionBLength; + } + + // check for path length and prefer shorter + const pathA = accessor.getItemPath(itemA); + const pathB = accessor.getItemPath(itemB); + + if (pathA && pathB && pathA.length !== pathB.length) { + return pathA.length - pathB.length; + } + + // 7.) finally we have equal scores and equal length, we fallback to comparer + + // compare by label + if (labelA !== labelB) { + return compareAnything(labelA, labelB, query.normalized); + } + + // compare by description + if (descriptionA && descriptionB && descriptionA !== descriptionB) { + return compareAnything(descriptionA, descriptionB, query.normalized); + } + + // compare by path + if (pathA && pathB && pathA !== pathB) { + 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 78fc5e97ce1ec..5947cb70276db 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -251,14 +251,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 +302,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 +339,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 +353,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 +374,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; @@ -398,7 +398,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 +409,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 +457,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 +540,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); @@ -565,7 +565,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++) { @@ -620,12 +620,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 1902e82c3121c..d5770516836b8 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -3,10 +3,17 @@ * 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 +29,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 +53,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 +73,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 fb146b969f51f..f7b8d5ed64d55 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) { diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index c9bfed7ea8fbe..9e8a13a3a3937 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -5,37 +5,54 @@ 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; readonly isTrusted?: boolean; + readonly supportThemeIcons?: boolean; uris?: { [href: string]: UriComponents }; } export class MarkdownString implements IMarkdownString { + private readonly _isTrusted: boolean; + private readonly _supportThemeIcons: boolean; + + constructor( + private _value: string = '', + isTrustedOrOptions: boolean | { isTrusted?: boolean, supportThemeIcons?: boolean } = false, + ) { + if (typeof this._value !== 'string') { + throw illegalArgument('value'); + } - private _value: string; - private _isTrusted: boolean; - - constructor(value: string = '', isTrusted = false) { - this._value = value; - this._isTrusted = isTrusted; + if (typeof isTrustedOrOptions === 'boolean') { + this._isTrusted = isTrustedOrOptions; + this._supportThemeIcons = false; + } + else { + this._isTrusted = isTrustedOrOptions.isTrusted ?? false; + this._supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; + } } get value() { return this._value; } get isTrusted() { return this._isTrusted; } + get supportThemeIcons() { return this._supportThemeIcons; } appendText(value: string): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this._value += value + this._value += (this._supportThemeIcons ? escapeCodicons(value) : value) .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') - .replace('\n', '\n\n'); + .replace(/\n/g, '\n\n'); return this; } appendMarkdown(value: string): MarkdownString { this._value += value; + return this; } @@ -64,7 +81,8 @@ export function isMarkdownString(thing: any): thing is IMarkdownString { return true; } else if (thing && typeof thing === 'object') { return typeof (thing).value === 'string' - && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === undefined); + && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === undefined) + && (typeof (thing).supportThemeIcons === 'boolean' || (thing).supportThemeIcons === undefined); } return false; } @@ -89,7 +107,7 @@ function markdownStringEqual(a: IMarkdownString, b: IMarkdownString): boolean { } else if (!a || !b) { return false; } else { - return a.value === b.value && a.isTrusted === b.isTrusted; + return a.value === b.value && a.isTrusted === b.isTrusted && a.supportThemeIcons === b.supportThemeIcons; } } diff --git a/src/vs/base/common/insane/insane.d.ts b/src/vs/base/common/insane/insane.d.ts index 9b5a77c3b8e0e..13fa1f2662b48 100644 --- a/src/vs/base/common/insane/insane.d.ts +++ b/src/vs/base/common/insane/insane.d.ts @@ -9,6 +9,7 @@ export function insane( 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, }, strict?: boolean, ): string; diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index f44cbd236b8fb..d66882ac09f7f 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -3,282 +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 interface Iterator { - next(): IteratorResult; -} - -interface NativeIteratorYieldResult { - done?: false; - value: TYield; -} - -interface NativeIteratorReturnResult { - done: true; - value: TReturn; -} - -type NativeIteratorResult = NativeIteratorYieldResult | NativeIteratorReturnResult; - -export interface NativeIterator { - next(): NativeIteratorResult; -} +export namespace Iterable { -export module Iterator { - const _empty: Iterator = { - next() { - return FIN; - } - }; + export function is(thing: any): thing is IterableIterator { + return thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function'; + } - 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 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 some(iterable: Iterable, predicate: (t: T) => boolean): boolean { + for (const element of iterable) { + if (predicate(element)) { + return true; } - }; + } + return false; } - 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* filter(iterable: Iterable, predicate: (t: T) => boolean): Iterable { + for (const element of iterable) { + if (predicate(element)) { + yield element; } - }; - } - - 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 collect(iterator: Iterator, atMost: number = Number.POSITIVE_INFINITY): T[] { - const result: T[] = []; - - if (atMost === 0) { - return result; + export function* map(iterable: Iterable, fn: (t: T) => R): Iterable { + for (const element of iterable) { + yield fn(element); } + } - let i = 0; - - for (let next = iterator.next(); !next.done; next = iterator.next()) { - result.push(next.value); - - if (++i >= atMost) { - break; + export function* concat(...iterables: Iterable[]): Iterable { + for (const iterable of iterables) { + for (const element of iterable) { + yield element; } } - - return result; } - export function concat(...iterators: Iterator[]): Iterator { - let i = 0; + /** + * 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[] = []; - return { - next() { - if (i >= iterators.length) { - return FIN; - } + if (atMost === 0) { + return [consumed, iterable]; + } - const iterator = iterators[i]; - const result = iterator.next(); + const iterator = iterable[Symbol.iterator](); - if (result.done) { - i++; - return this.next(); - } + for (let i = 0; i < atMost; i++) { + const next = iterator.next(); - return result; + if (next.done) { + return [consumed, Iterable.empty()]; } - }; - } -} -export type ISequence = Iterator | T[]; - -export function getSequenceIterator(arg: Iterator | T[]): Iterator { - if (Array.isArray(arg)) { - return Iterator.fromArray(arg); - } 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; + consumed.push(next.value); } - 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(); + return [consumed, { [Symbol.iterator]() { return iterator; } }]; } - - 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) => 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 df362c065893d..933e370f92cf9 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 0aac5a59b2220..a16f4457a1c3b 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -84,47 +84,43 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo return withFormatting(text, edit, formattingOptions); } } else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) { - const insertIndex = lastSegment; - if (insertIndex === -1) { + if (value !== undefined) { // Insert const newProperty = `${JSON.stringify(value)}`; let edit: Edit; - if (parent.children.length === 0) { - edit = { offset: parent.offset + 1, length: 0, content: newProperty }; + if (parent.children.length === 0 || lastSegment === 0) { + edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + ',' }; } else { - const previous = parent.children[parent.children.length - 1]; + const index = lastSegment === -1 || lastSegment > parent.children.length ? parent.children.length : lastSegment; + const previous = parent.children[index - 1]; edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty }; } return withFormatting(text, edit, formattingOptions); } else { - if (value === undefined && parent.children.length >= 0) { - //Removal - const removalIndex = lastSegment; - const toRemove = parent.children[removalIndex]; - let edit: Edit; - if (parent.children.length === 1) { - // only item - edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' }; - } else if (parent.children.length - 1 === removalIndex) { - // last item - const previous = parent.children[removalIndex - 1]; - const offset = previous.offset + previous.length; - const parentEndOffset = parent.offset + parent.length; - edit = { offset, length: parentEndOffset - 2 - offset, content: '' }; - } else { - edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' }; - } - return withFormatting(text, edit, formattingOptions); + //Removal + const removalIndex = lastSegment; + const toRemove = parent.children[removalIndex]; + let edit: Edit; + if (parent.children.length === 1) { + // only item + edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' }; + } else if (parent.children.length - 1 === removalIndex) { + // last item + const previous = parent.children[removalIndex - 1]; + const offset = previous.offset + previous.length; + const parentEndOffset = parent.offset + parent.length; + edit = { offset, length: parentEndOffset - 2 - offset, content: '' }; } else { - throw new Error('Array modification not supported yet'); + edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' }; } + return withFormatting(text, edit, formattingOptions); } } else { throw new Error(`Can not add ${typeof lastSegment !== 'number' ? 'index' : 'property'} to parent of type ${parent.type}`); } } -function withFormatting(text: string, edit: Edit, formattingOptions: FormattingOptions): Edit[] { +export function withFormatting(text: string, edit: Edit, formattingOptions: FormattingOptions): Edit[] { // apply the edit let newText = applyEdit(text, edit); @@ -179,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/jsonFormatter.ts b/src/vs/base/common/jsonFormatter.ts index a25c493c05d9a..05852ef5fc5c4 100644 --- a/src/vs/base/common/jsonFormatter.ts +++ b/src/vs/base/common/jsonFormatter.ts @@ -228,7 +228,7 @@ function computeIndentLevel(content: string, options: FormattingOptions): number return Math.floor(nChars / tabSize); } -function getEOL(options: FormattingOptions, text: string): string { +export function getEOL(options: FormattingOptions, text: string): string { for (let i = 0; i < text.length; i++) { const ch = text.charAt(i); if (ch === '\r') { @@ -245,4 +245,4 @@ function getEOL(options: FormattingOptions, text: string): string { export function isEOL(text: string, offset: number) { return '\r\n'.indexOf(text.charAt(offset)) !== -1; -} \ No newline at end of file +} diff --git a/src/vs/base/common/jsonSchema.ts b/src/vs/base/common/jsonSchema.ts index 58fa1f7287ffa..da614983689e2 100644 --- a/src/vs/base/common/jsonSchema.ts +++ b/src/vs/base/common/jsonSchema.ts @@ -3,11 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +export type JSONSchemaType = 'string' | 'number' | 'integer' | 'boolean' | 'null' | 'array' | 'object'; + export interface IJSONSchema { id?: string; $id?: string; $schema?: string; - type?: string | string[]; + type?: JSONSchemaType | JSONSchemaType[]; title?: string; default?: any; definitions?: IJSONSchemaMap; @@ -51,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 671816830bdf7..1cdbc8d8e9e39 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 86a05d80658d5..5f3fab864908a 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 fe88e82bc80b5..ce6339106ef43 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, @@ -54,6 +54,11 @@ export class Lazy { return this._value!; } + /** + * Get the wrapped value without forcing evaluation. + */ + get rawValue(): T | undefined { return this._value; } + /** * Create a new lazy value that is the result of applying `f` to the wrapped value. * diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 8d1cbd5b99680..79bbb910aac37 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); @@ -163,7 +192,7 @@ export abstract class Disposable implements IDisposable { /** * Manages the lifecycle of a disposable value that may be changed. * - * This ensures that when the the disposable value is changed, the previously held disposable is disposed of. You can + * This ensures that when the disposable value is changed, the previously held disposable is disposed of. You can * also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up. */ export class MutableDisposable implements IDisposable { @@ -206,43 +235,6 @@ export class MutableDisposable implements IDisposable { } } -/** - * Wrapper class that stores a disposable that is not currently "owned" by anyone. - * - * Example use cases: - * - * - Express that a function/method will take ownership of a disposable parameter. - * - Express that a function returns a disposable that the caller must explicitly take ownership of. - */ -export class UnownedDisposable extends Disposable { - private _hasBeenAcquired = false; - private _value?: T; - - public constructor(value: T) { - super(); - this._value = value; - } - - public acquire(): T { - if (this._hasBeenAcquired) { - throw new Error('This disposable has already been acquired'); - } - this._hasBeenAcquired = true; - const value = this._value!; - this._value = undefined; - return value; - } - - public dispose() { - super.dispose(); - if (!this._hasBeenAcquired) { - this._hasBeenAcquired = true; - this._value!.dispose(); - this._value = undefined; - } - } -} - export interface IReference extends IDisposable { readonly object: T; } @@ -251,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); } @@ -272,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 54bb9253de60e..8ca17bc73f990 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 0000000000000..5f6c6a05dcf55 --- /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 dadb16862bb15..0b8c7460f7962 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -5,23 +5,9 @@ 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 } from 'vs/base/common/strings'; +import { Schemas } from 'vs/base/common/network'; +import { isLinux } from 'vs/base/common/platform'; export function getOrSet(map: Map, key: K, value: V): V { let result = map.get(key); @@ -51,28 +37,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 +46,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 +77,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,61 +119,132 @@ export class PathIterator implements IKeyIterator { } cmp(a: string): number { + 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 { + return this._value.substring(this._from, this._to); + } +} + +const enum UriIteratorState { + Scheme = 1, Authority = 2, Path = 3, Query = 4, Fragment = 5 +} - let aPos = 0; - const aLen = a.length; - let thisPos = this._from; +export class UriIterator implements IKeyIterator { - while (aPos < aLen && thisPos < this._to) { - const cmp = a.charCodeAt(aPos) - this._value.charCodeAt(thisPos); - if (cmp !== 0) { - return cmp; + private _pathIterator!: PathIterator; + private _value!: URI; + private _states: UriIteratorState[] = []; + private _stateIdx: number = 0; + + 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) { + //todo@jrieken the case-sensitive logic is copied form `resources.ts#hasToIgnoreCase` + // which cannot be used because it depends on this + const caseSensitive = key.scheme === Schemas.file && isLinux; + this._pathIterator = new PathIterator(false, caseSensitive); + this._pathIterator.reset(key.path); + if (this._pathIterator.value()) { + this._states.push(UriIteratorState.Path); } - aPos += 1; - thisPos += 1; } + if (this._value.query) { + this._states.push(UriIteratorState.Query); + } + if (this._value.fragment) { + this._states.push(UriIteratorState.Fragment); + } + this._stateIdx = 0; + return this; + } - if (aLen === this._to - this._from) { - return 0; - } else if (aPos < aLen) { - return -1; + next(): this { + if (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) { + this._pathIterator.next(); } else { - return 1; + 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 compare(a, this._value.scheme); + } else if (this._states[this._stateIdx] === UriIteratorState.Authority) { + return compareSubstringIgnoreCase(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 { - return this._value.substring(this._from, this._to); + 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 { +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 forUris(): TernarySearchTree { + return new TernarySearchTree(new UriIterator()); + } - static forPaths(): TernarySearchTree { - return new TernarySearchTree(new PathIterator()); + static forPaths(): TernarySearchTree { + return new TernarySearchTree(new PathIterator()); } - static forStrings(): TernarySearchTree { - return new TernarySearchTree(new StringIterator()); + static forStrings(): TernarySearchTree { + return new TernarySearchTree(new StringIterator()); } - private _iter: IKeyIterator; - private _root: TernarySearchTreeNode | undefined; + private _iter: IKeyIterator; + private _root: TernarySearchTreeNode | undefined; - constructor(segments: IKeyIterator) { + constructor(segments: IKeyIterator) { this._iter = segments; } @@ -212,12 +252,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 +267,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 +275,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 +284,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 +298,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 +320,10 @@ export class TernarySearchTree { return node ? node.value : undefined; } - delete(key: string): void { + delete(key: K): 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 @@ -321,10 +361,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 +385,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) { @@ -372,11 +412,11 @@ export class TernarySearchTree { return undefined; } - private _nodeIterator(node: TernarySearchTreeNode): Iterator { - let res: { done: false; value: E; }; + private _nodeIterator(node: TernarySearchTreeNode): Iterator { + let res: { done: false; value: V; }; let idx: number; - let data: E[]; - const next = (): IteratorResult => { + let data: V[]; + const next = (): IteratorResult => { if (!data) { // lazy till first invocation data = []; @@ -384,7 +424,7 @@ export class TernarySearchTree { this._forEach(node, value => data.push(value)); } if (idx >= data.length) { - return FIN; + return { done: true, value: undefined }; } if (!res) { @@ -397,11 +437,11 @@ export class TernarySearchTree { return { next }; } - forEach(callback: (value: E, index: string) => any) { + forEach(callback: (value: V, index: K) => any) { this._forEach(this._root, callback); } - private _forEach(node: TernarySearchTreeNode | undefined, callback: (value: E, index: string) => any) { + private _forEach(node: TernarySearchTreeNode | undefined, callback: (value: V, index: K) => any) { if (node) { // left this._forEach(node.left, callback); @@ -420,18 +460,45 @@ export class TernarySearchTree { } } -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 +521,35 @@ export class ResourceMap { return this.map.delete(this.toKey(resource)); } - forEach(clb: (value: T) => void): void { - this.map.forEach(clb); + 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); + } } - values(): T[] { - return values(this.map); + values(): IterableIterator { + return this.map.values(); } - private toKey(resource: URI): string { - let key = resource.toString(); - if (this.ignoreCase) { - key = key.toLowerCase(); + *keys(): IterableIterator { + for (let key of this.map.keys()) { + yield URI.parse(key); } - - return key; } - keys(): URI[] { - return keys(this.map).map(k => URI.parse(k)); + *entries(): IterableIterator<[URI, T]> { + for (let tuple of this.map.entries()) { + yield [URI.parse(tuple[0]), tuple[1]]; + } } - clone(): ResourceMap { - const resourceMap = new ResourceMap(); - - this.map.forEach((value, key) => resourceMap.map.set(key, value)); - - return resourceMap; + *[Symbol.iterator](): IterableIterator<[URI, T]> { + for (let item of this.map) { + yield [URI.parse(item[0]), item[1]]; + } } } @@ -497,18 +566,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 +590,7 @@ export class LinkedMap { this._head = undefined; this._tail = undefined; this._size = 0; + this._state++; } isEmpty(): boolean { @@ -526,6 +601,14 @@ export class LinkedMap { return this._size; } + get first(): V | undefined { + return this._head?.value; + } + + get last(): V | undefined { + return this._tail?.value; + } + has(key: K): boolean { return this._map.has(key); } @@ -541,7 +624,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; @@ -567,6 +650,7 @@ export class LinkedMap { this._map.set(key, item); this._size++; } + return this; } delete(key: K): boolean { @@ -599,6 +683,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) { @@ -606,38 +691,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; @@ -651,12 +723,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; @@ -668,7 +745,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) { @@ -690,6 +794,7 @@ export class LinkedMap { if (current) { current.previous = undefined; } + this._state++; } private addItemFirst(item: Item): void { @@ -703,6 +808,7 @@ export class LinkedMap { this._head.previous = item; } this._head = item; + this._state++; } private addItemLast(item: Item): void { @@ -716,6 +822,7 @@ export class LinkedMap { this._tail.next = item; } this._tail = item; + this._state++; } private removeItem(item: Item): void { @@ -752,6 +859,7 @@ export class LinkedMap { } item.next = undefined; item.previous = undefined; + this._state++; } private touch(item: Item, touch: Touch): void { @@ -788,6 +896,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; @@ -811,6 +920,7 @@ export class LinkedMap { item.previous = this._tail; this._tail.next = item; this._tail = item; + this._state++; } } @@ -862,17 +972,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 1288f459647b0..fb133b90fb8a7 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -1,1714 +1,2627 @@ /** * 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]+/ -}; - -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(); +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.marked = factory()); +}(this, (function () { 'use strict'; + + 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); + } + } -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(); + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } -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(); + 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); + } -block.blockquote = edit(block.blockquote) - .replace('paragraph', block.paragraph) - .getRegex(); + function _arrayLikeToArray(arr, len) { + if (len == null || len > arr.length) len = arr.length; -/** - * Normal Block Grammar - */ + for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; -block.normal = merge({}, block); + return arr2; + } -/** - * GFM Block Grammar - */ + function _createForOfIteratorHelperLoose(o, allowArrayLike) { + var it; + + 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++] + }; + }; + } -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+|$)/ -}); + throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); + } -block.gfm.paragraph = edit(block.paragraph) - .replace('(?!', '(?!' - + block.gfm.fences.source.replace('\\1', '\\2') + '|' - + block.list.source.replace('\\1', '\\3') + '|') - .getRegex(); + it = o[Symbol.iterator](); + return it.next.bind(it); + } -/** - * GFM + Tables Block Grammar - */ + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } -block.tables = merge({}, block.gfm, { - nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/, - table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/ -}); + 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 + }; + } -/** - * Pedantic grammar - */ + function changeDefaults(newDefaults) { + module.exports.defaults = newDefaults; + } -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+|$)/ -}); + 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 = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; -/** - * Block Lexer - */ + var getEscapeReplacement = function getEscapeReplacement(ch) { + return escapeReplacements[ch]; + }; -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; + function escape(html, encode) { + if (encode) { + if (escapeTest.test(html)) { + return html.replace(escapeReplace, getEscapeReplacement); + } } else { - this.rules = block.gfm; + if (escapeTestNoEncode.test(html)) { + return html.replace(escapeReplaceNoEncode, getEscapeReplacement); + } } + + return html; } -} -/** - * Expose Block Rules - */ + var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig; -Lexer.rules = block; + function unescape(html) { + // explicitly match decimal, hex, and named HTML entities + return html.replace(unescapeTest, function (_, n) { + n = n.toLowerCase(); + if (n === 'colon') return ':'; -/** - * Static Lex Method - */ + if (n.charAt(0) === '#') { + return n.charAt(1) === 'x' ? String.fromCharCode(parseInt(n.substring(2), 16)) : String.fromCharCode(+n.substring(1)); + } -Lexer.lex = function(src, options) { - var lexer = new Lexer(options); - return lexer.lex(src); -}; + return ''; + }); + } -/** - * Preprocessing - */ + var caret = /(^|[^\[])\^/g; + + 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; + } -Lexer.prototype.lex = function(src) { - src = src - .replace(/\r\n|\r/g, '\n') - .replace(/\t/g, ' ') - .replace(/\u00a0/g, ' ') - .replace(/\u2424/g, '\n'); + var nonWordAndColonTest = /[^\w:]/g; + var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i; - return this.token(src, true); -}; + function cleanUrl(sanitize, base, href) { + if (sanitize) { + var prot; -/** - * Lexing - */ + try { + prot = decodeURIComponent(unescape(href)).replace(nonWordAndColonTest, '').toLowerCase(); + } catch (e) { + return null; + } -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; - - 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' - }); + if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) { + return null; } } - // 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; + if (base && !originIndependentUrl.test(href)) { + href = resolveUrl(base, href); } - // 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; + try { + href = encodeURI(href).replace(/%25/g, '%'); + } catch (e) { + return null; } - // 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; + return href; + } + + 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); + } } - // 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') : [] - }; + base = baseUrls[' ' + base]; + var relativeBase = base.indexOf(':') === -1; - if (item.header.length === item.align.length) { - src = src.substring(cap[0].length); + if (href.substring(0, 2) === '//') { + if (relativeBase) { + return href; + } - 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; - } - } + 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() {} + }; - for (i = 0; i < item.cells.length; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); + 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; - this.tokens.push(item); + while (--curr >= 0 && str[curr] === '\\') { + escaped = !escaped; + } - continue; + 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(''); } } - // hr - if (cap = this.rules.hr.exec(src)) { - src = src.substring(cap[0].length); - this.tokens.push({ - type: 'hr' - }); - continue; + for (; i < cells.length; i++) { + // leading or trailing whitespace is ignored per the gfm spec + cells[i] = cells[i].trim().replace(/\\\|/g, '|'); } - // blockquote - if (cap = this.rules.blockquote.exec(src)) { - src = src.substring(cap[0].length); + 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. - this.tokens.push({ - type: 'blockquote_start' - }); - cap = cap[0].replace(/^ *> ?/gm, ''); + function rtrim(str, c, invert) { + var l = str.length; - // Pass `top` to keep the current - // "toplevel" state. This is exactly - // how markdown.pl works. - this.token(cap, top); + if (l === 0) { + return ''; + } // Length of suffix matching the invert condition. - this.tokens.push({ - type: 'blockquote_end' - }); - continue; + 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; + } + } } - // list - if (cap = this.rules.list.exec(src)) { - src = src.substring(cap[0].length); - bull = cap[2]; - isordered = bull.length > 1; - - listStart = { - type: 'list_start', - ordered: isordered, - start: isordered ? +bull : '', - loose: false + return -1; + } + + function checkSanitizeDeprecation(opt) { + if (opt && opt.sanitize && !opt.silent) { + // VS CODE CHANGE + // Disable logging about sanitize options. We already use insane after running the sanitizer + + // 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'); + } + } + + var helpers = { + escape: escape, + unescape: unescape, + edit: edit, + cleanUrl: cleanUrl, + resolveUrl: resolveUrl, + noopTest: noopTest, + merge: merge, + splitCells: splitCells, + rtrim: rtrim, + findClosingBracket: findClosingBracket, + checkSanitizeDeprecation: checkSanitizeDeprecation + }; + + 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) + }; + } + } + + function indentCodeCompensation(raw, text) { + var matchIndentToCode = raw.match(/^(\s+)(?:```)/); + + if (matchIndentToCode === null) { + return text; + } + + var indentToCode = matchIndentToCode[1]; + return text.split('\n').map(function (node) { + var matchIndentInNode = node.match(/^\s+/); + + if (matchIndentInNode === null) { + return node; + } - this.tokens.push(listStart); + var indentInNode = matchIndentInNode[0]; - // Get each top-level item. - cap = cap[0].match(this.rules.item); + if (indentInNode.length >= indentToCode.length) { + return node.slice(indentToCode.length); + } + + return node; + }).join('\n'); + } + /** + * Tokenizer + */ - listItems = []; - next = false; - l = cap.length; - i = 0; - for (; i < l; i++) { - item = cap[i]; + var Tokenizer_1 = /*#__PURE__*/function () { + function Tokenizer(options) { + this.options = options || defaults$1; + } - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+\.) */, ''); + var _proto = Tokenizer.prototype; - // 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, ''); + _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() + }; + } + + var text = cap[0].replace(/^ {4}/gm, ''); + return { + type: 'code', + raw: cap[0], + codeBlockStyle: 'indented', + text: !this.options.pedantic ? rtrim$1(text, '\n') : text + }; + } + }; + + _proto.fences = function fences(src) { + var cap = this.rules.block.fences.exec(src); + + 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 + }; + } + }; + + _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] + }; + + if (item.header.length === item.align.length) { + 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], item.header.length); } + + return item; } + } + }; - // 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; + _proto.hr = function hr(src) { + var cap = this.rules.block.hr.exec(src); + + if (cap) { + return { + type: 'hr', + raw: cap[0] + }; + } + }; + + _proto.blockquote = function blockquote(src) { + var cap = this.rules.block.blockquote.exec(src); + + 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 + }); } - if (loose) { - listStart.loose = true; + 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] + }; + } + }; + + _proto.table = function table(src) { + var cap = this.rules.block.table.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') : [] + }; + + 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); - // Check for task list items - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; - if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); + 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] + }; } - t = { - type: 'list_item_start', - task: istask, - checked: ischecked, - loose: loose + return { + type: 'text', + raw: cap[0], + text: cap[0] }; + } + }; - listItems.push(t); - this.tokens.push(t); + _proto.escape = function escape(src) { + var cap = this.rules.inline.escape.exec(src); - // Recurse. - this.token(item, false); + 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; + } - this.tokens.push({ - type: 'list_item_end' - }); + 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] + }; } + }; - if (listStart.loose) { - l = listItems.length; - i = 0; - for (; i < l; i++) { - listItems[i].loose = true; + _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 { + 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; } + }; - this.tokens.push({ - type: 'list_end' - }); + _proto.reflink = function reflink(src, links) { + var cap; - continue; - } + 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()]; - // 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; - } + if (!link || !link.href) { + var text = cap[0].charAt(0); + return { + type: 'text', + raw: text, + text: text + }; + } - // 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] = { - href: cap[2], - title: cap[3] + 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 }; } - 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.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 (item.header.length === item.align.length) { - src = src.substring(cap[0].length); + 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; + } - 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'; + 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 { - item.align[i] = null; + 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); + return { + type: 'text', + raw: cap[0], + text: text + }; + } + }; + + return Tokenizer; + }(); + + var noopTest$1 = helpers.noopTest, + edit$1 = helpers.edit, + merge$1 = helpers.merge; + /** + * Block-Level Grammar + */ + + 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 + */ + + block.normal = merge$1({}, block); + /** + * GFM Block Grammar + */ + + block.gfm = merge$1({}, block.normal, { + nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header + + ' *([-:]+ *\\|[-| :]*)' // Align + + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', + // Cells + table: '^ *\\|(.+)\\n' // Header + + ' *\\|?( *[-:]+[-| :]*)' // Align + + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells + + }); + 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) + */ + + 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)\]\(\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) + + }, + 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) - continue; + }, + code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/, + br: /^( {2,}|\\)\n(?!\s*$)/, + del: noopTest$1, + text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~'; + inline.punctuation = edit$1(inline.punctuation).replace(/punctuation/g, inline._punctuation).getRegex(); // sequences em should skip over [title](link), `code`, + + 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(/blockSkip/g, inline._blockSkip).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.normal = merge$1({}, inline); + /** + * Pedantic Inline Grammar + */ + + 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.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: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\ 0.5) { + ch = 'x' + ch.toString(16); } - } - // 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; + out += '&#' + ch + ';'; } - // 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; + return out; + } + /** + * Block Lexer + */ + + + 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 + }; + + if (this.options.pedantic) { + rules.block = block$1.pedantic; + rules.inline = inline$1.pedantic; + } else if (this.options.gfm) { + rules.block = block$1.gfm; + + if (this.options.breaks) { + rules.inline = inline$1.breaks; + } else { + rules.inline = inline$1.gfm; + } + } + + this.tokenizer.rules = rules; } + /** + * Expose Rules + */ - // 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; + + /** + * Static Lex Method + */ + Lexer.lex = function lex(src, options) { + var lexer = new Lexer(options); + return lexer.lex(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 = []; + } + + 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 (src) { - throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); + 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 = []; + } - return this.tokens; -}; + if (inLink === void 0) { + inLink = false; + } -/** - * Inline-Level Grammar - */ + if (inRawBlock === void 0) { + inRawBlock = false; + } -var inline = { - escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/, - autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/, - url: noop, - 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]*?(?:(?=[\\?@\\[^_{|}~'; -inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).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(inline.autolink) - .replace('scheme', inline._scheme) - .replace('email', inline._email) - .getRegex(); + if (prevChar === void 0) { + prevChar = ''; + } -inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/; + var token; // String with links masked to avoid interference with em and strong -inline.tag = edit(inline.tag) - .replace('comment', block._comment) - .replace('attribute', inline._attribute) - .getRegex(); + var maskedSrc = src; + var match; // Mask out reflinks -inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|`(?!`)|[^\[\]\\`])*?/; -inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)/; -inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/; + if (this.tokens.links) { + var links = Object.keys(this.tokens.links); -inline.link = edit(inline.link) - .replace('label', inline._label) - .replace('href', inline._href) - .replace('title', inline._title) - .getRegex(); + 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) + '[' + 'a'.repeat(match[0].length - 2) + ']' + maskedSrc.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex); + } + } + } + } // Mask out other blocks -inline.reflink = edit(inline.reflink) - .replace('label', inline._label) - .getRegex(); -/** - * Normal Inline Grammar - */ + while ((match = this.tokenizer.rules.inline.blockSkip.exec(maskedSrc)) != null) { + maskedSrc = maskedSrc.slice(0, match.index) + '[' + 'a'.repeat(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); + } -inline.normal = merge({}, inline); + tokens.push(token); + continue; + } // strong -/** - * Pedantic Inline Grammar - */ -inline.pedantic = merge({}, inline.normal, { - strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, - em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/, - link: edit(/^!?\[(label)\]\((.*?)\)/) - .replace('label', inline._label) - .getRegex(), - reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/) - .replace('label', inline._label) - .getRegex() -}); + 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 -/** - * GFM Inline Grammar - */ -inline.gfm = merge({}, inline.normal, { - escape: edit(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: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\/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; - } + if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) { + src = src.substring(token.raw.length); + prevChar = token.raw.slice(-1); + tokens.push(token); + 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); + if (src) { + var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); - if (link) { - href = link[1]; - title = link[3]; - } else { - title = ''; + if (this.options.silent) { + console.error(errMsg); + break; + } else { + throw new Error(errMsg); + } } - } 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; - } + return tokens; + }; - // 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; - } + _createClass(Lexer, null, [{ + key: "rules", + get: function get() { + return { + block: block$1, + inline: inline$1 + }; + } + }]); - // 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; - } + return Lexer; + }(); - // code - if (cap = this.rules.code.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.codespan(escape(cap[2].trim(), true)); - continue; - } + var defaults$3 = defaults.defaults; + var cleanUrl$1 = helpers.cleanUrl, + escape$1 = helpers.escape; + /** + * Renderer + */ - // br - if (cap = this.rules.br.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.br(); - continue; + var Renderer_1 = /*#__PURE__*/function () { + function Renderer(options) { + this.options = options || defaults$3; } - // del (gfm) - if (cap = this.rules.del.exec(src)) { - src = src.substring(cap[0].length); - out += this.renderer.del(this.output(cap[1])); - continue; - } + var _proto = Renderer.prototype; - // 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; - } + _proto.code = function code(_code, infostring, escaped) { + var lang = (infostring || '').match(/\S*/)[0]; - // 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; + if (this.options.highlight) { + var out = this.options.highlight(_code, lang); + + if (out != null && out !== _code) { + escaped = true; + _code = out; } } - 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]))); + if (!lang) { + return '
' + (escaped ? _code : escape$1(_code, true)) + '
\n'; } - continue; - } - if (src) { - throw new Error('Infinite loop on byte: ' + src.charCodeAt(0)); - } - } + return '
' + (escaped ? _code : escape$1(_code, true)) + '
\n'; + }; - return out; -}; + _proto.blockquote = function blockquote(quote) { + return '
\n' + quote + '
\n'; + }; -InlineLexer.escapes = function(text) { - return text ? text.replace(InlineLexer.rules._escapes, '$1') : text; -}; + _proto.html = function html(_html) { + return _html; + }; -/** - * Compile Link - */ + _proto.heading = function heading(text, level, raw, slugger) { + if (this.options.headerIds) { + return '' + text + '\n'; + } // ignore IDs -InlineLexer.prototype.outputLink = function(cap, link) { - var href = link.href, - title = link.title ? escape(link.title) : null; - return cap[0].charAt(0) !== '!' - ? this.renderer.link(href, title, this.output(cap[1])) - : this.renderer.image(href, title, escape(cap[1])); -}; + return '' + text + '\n'; + }; -/** - * Smartypants Transformations - */ + _proto.hr = function hr() { + return this.options.xhtml ? '
\n' : '
\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.list = function list(body, ordered, start) { + var type = ordered ? 'ol' : 'ul', + startatt = ordered && start !== 1 ? ' start="' + start + '"' : ''; + return '<' + type + startatt + '>\n' + body + '\n'; + }; -/** - * Mangle Links - */ + _proto.listitem = function listitem(text) { + return '
  • ' + text + '
  • \n'; + }; -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 + ';'; - } + _proto.checkbox = function checkbox(checked) { + return ' '; + }; - return out; -}; + _proto.paragraph = function paragraph(text) { + return '

    ' + text + '

    \n'; + }; -/** - * Renderer - */ + _proto.table = function table(header, body) { + if (body) body = '' + body + ''; + return '\n' + '\n' + header + '\n' + body + '
    \n'; + }; -function Renderer(options) { - this.options = options || marked.defaults; -} - -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.tablerow = function tablerow(content) { + return '\n' + content + '\n'; + }; - if (!lang) { - return '
    '
    -      + (escaped ? code : escape(code, true))
    -      + '
    '; - } + _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 + ; - return '
    '
    -    + (escaped ? code : escape(code, true))
    -    + '
    \n'; -}; - -Renderer.prototype.blockquote = function(quote) { - return '
    \n' + quote + '
    \n'; -}; - -Renderer.prototype.html = function(html) { - return html; -}; - -Renderer.prototype.heading = function(text, level, raw, slugger) { - if (this.options.headerIds) { - return '' - + text - + '\n'; - } - // ignore IDs - return '' + text + '\n'; -}; - -Renderer.prototype.hr = function() { - return this.options.xhtml ? '
    \n' : '
    \n'; -}; - -Renderer.prototype.list = function(body, ordered, start) { - var type = ordered ? 'ol' : 'ul', - startatt = (ordered && start !== 1) ? (' start="' + start + '"') : ''; - return '<' + type + startatt + '>\n' + body + '\n'; -}; - -Renderer.prototype.listitem = function(text) { - return '
  • ' + text + '
  • \n'; -}; - -Renderer.prototype.checkbox = function(checked) { - return ' '; -}; - -Renderer.prototype.paragraph = function(text) { - return '

    ' + text + '

    \n'; -}; - -Renderer.prototype.table = function(header, body) { - if (body) body = '' + body + ''; - - return '\n' - + '\n' - + header - + '\n' - + body - + '
    \n'; -}; - -Renderer.prototype.tablerow = function(content) { - return '\n' + content + '\n'; -}; - -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'; -}; - -// span level renderer -Renderer.prototype.strong = function(text) { - return '' + text + ''; -}; - -Renderer.prototype.em = function(text) { - return '' + text + ''; -}; - -Renderer.prototype.codespan = function(text) { - return '' + text + ''; -}; - -Renderer.prototype.br = function() { - return this.options.xhtml ? '
    ' : '
    '; -}; - -Renderer.prototype.del = function(text) { - return '' + text + ''; -}; - -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; -}; - -Renderer.prototype.image = function(href, title, text) { - href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); - if (href === null) { - return text; - } + _proto.strong = function strong(text) { + return '' + text + ''; + }; - var out = '' + text + '' : '>'; - return out; -}; + _proto.em = function em(text) { + return '' + text + ''; + }; -Renderer.prototype.text = function(text) { - return text; -}; + _proto.codespan = function codespan(text) { + return '' + text + ''; + }; -/** - * TextRenderer - * returns only the textual part of the token - */ + _proto.br = function br() { + return this.options.xhtml ? '
    ' : '
    '; + }; -function TextRenderer() {} + _proto.del = function del(text) { + return '' + text + ''; + }; -// no need for block level renderers + _proto.link = function link(href, title, text) { + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); -TextRenderer.prototype.strong = -TextRenderer.prototype.em = -TextRenderer.prototype.codespan = -TextRenderer.prototype.del = -TextRenderer.prototype.text = function (text) { - return text; -}; + if (href === null) { + return text; + } -TextRenderer.prototype.link = -TextRenderer.prototype.image = function(href, title, text) { - return '' + text; -}; + var out = '
    '; + return out; + }; -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) { + href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href); -/** - * Static Parse Method - */ + if (href === null) { + return text; + } -Parser.parse = function(src, options) { - var parser = new Parser(options); - return parser.parse(src); -}; + var out = '' + text + '' : '>'; + return out; + }; - return out; -}; + _proto.text = function text(_text) { + return _text; + }; -/** - * Next Token - */ + return Renderer; + }(); -Parser.prototype.next = function() { - return this.token = this.tokens.pop(); -}; + /** + * TextRenderer + * returns only the textual part of the token + */ + var TextRenderer_1 = /*#__PURE__*/function () { + function TextRenderer() {} -/** - * Preview Next Token - */ + var _proto = TextRenderer.prototype; + + // no need for block level renderers + _proto.strong = function strong(text) { + return text; + }; -Parser.prototype.peek = function() { - return this.tokens[this.tokens.length - 1] || 0; -}; + _proto.em = function em(text) { + return text; + }; -/** - * Parse Text Tokens - */ + _proto.codespan = function codespan(text) { + return text; + }; -Parser.prototype.parseText = function() { - var body = this.token.text; + _proto.del = function del(text) { + return text; + }; - while (this.peek().type === 'text') { - body += '\n' + this.next().text; - } + _proto.html = function html(text) { + return text; + }; - return this.inline.output(body); -}; + _proto.text = function text(_text) { + return _text; + }; -/** - * Parse Current Token - */ + _proto.link = function link(href, title, text) { + return '' + text; + }; -Parser.prototype.tok = function() { - switch (this.token.type) { - case 'space': { + _proto.image = function image(href, title, text) { + return '' + text; + }; + + _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(); - } - case 'heading': { - return this.renderer.heading( - this.inline.output(this.token.text), - this.token.depth, - unescape(this.inlineText.output(this.token.text)), - this.slugger); - } - case 'code': { - return this.renderer.code(this.token.text, - this.token.lang, - this.token.escaped); + + 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 'table': { - var header = '', - body = '', - i, - row, - cell, - j; - - // 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 < this.token.cells.length; i++) { - row = this.token.cells[i]; - - cell = ''; - for (j = 0; j < row.length; j++) { - cell += this.renderer.tablecell( - this.inline.output(row[j]), - { header: false, align: this.token.align[j] } - ); - } + /** + * 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)); + } - body += this.renderer.tablerow(cell); + if (!isDryRun) { + this.seen[originalSlug] = occurenceAccumulator; + this.seen[slug] = 0; } - return this.renderer.table(header, body); - } - case 'blockquote_start': { - body = ''; - while (this.next().type !== 'blockquote_end') { - body += this.tok(); + return slug; + } + /** + * 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 = {}; } - return this.renderer.blockquote(body); + 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 'list_start': { - body = ''; - var ordered = this.token.ordered, - start = this.token.start; + /** + * Static Parse Method + */ - while (this.next().type !== 'list_end') { - body += this.tok(); - } - return this.renderer.list(body, ordered, start); + Parser.parse = function parse(tokens, options) { + var parser = new Parser(options); + return parser.parse(tokens); } - case 'list_item_start': { - body = ''; - var loose = this.token.loose; - var checked = this.token.checked; - var task = this.token.task; + /** + * Parse Loop + */ + ; + + var _proto = Parser.prototype; - if (this.token.task) { - body += this.renderer.checkbox(checked); + _proto.parse = function parse(tokens, top) { + if (top === void 0) { + top = true; } - while (this.next().type !== 'list_item_end') { - body += !loose && this.token.type === 'text' - ? this.parseText() - : this.tok(); + var out = '', + i, + j, + k, + l2, + l3, + row, + cell, + header, + body, + token, + ordered, + start, + loose, + itemBody, + item, + checked, + task, + checkbox; + var l = tokens.length; + + for (i = 0; i < l; i++) { + token = tokens[i]; + + switch (token.type) { + case 'space': + { + continue; + } + + 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); + } + } + } } - return this.renderer.listitem(body, task, checked); - } - case 'html': { - // TODO parse inline content if parameter markdown=1 - return this.renderer.html(this.token.text); + + return out; } - case 'paragraph': { - return this.renderer.paragraph(this.inline.output(this.token.text)); + /** + * Parse Inline Tokens + */ + ; + + _proto.parseInline = function parseInline(tokens, renderer) { + renderer = renderer || this.renderer; + var out = '', + i, + token; + var l = tokens.length; + + for (i = 0; i < l; i++) { + token = tokens[i]; + + switch (token.type) { + case 'escape': + { + out += renderer.text(token.text); + break; + } + + case 'html': + { + out += renderer.html(token.text); + break; + } + + case 'link': + { + out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer)); + break; + } + + case 'image': + { + out += renderer.image(token.href, token.title, token.text); + break; + } + + case 'strong': + { + out += renderer.strong(this.parseInline(token.tokens, renderer)); + break; + } + + case 'em': + { + out += renderer.em(this.parseInline(token.tokens, renderer)); + break; + } + + case 'codespan': + { + out += renderer.codespan(token.text); + break; + } + + case 'br': + { + out += renderer.br(); + break; + } + + case 'del': + { + out += renderer.del(this.parseInline(token.tokens, renderer)); + break; + } + + case 'text': + { + out += renderer.text(token.text); + break; + } + + default: + { + var errMsg = 'Token with "' + token.type + '" type was not found.'; + + if (this.options.silent) { + console.error(errMsg); + return; + } else { + throw new Error(errMsg); + } + } + } + } + + 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'); } - case 'text': { - return this.renderer.paragraph(this.parseText()); + + if (typeof src !== 'string') { + throw new Error('marked(): input parameter is of type ' + Object.prototype.toString.call(src) + ', string expected'); } - default: { - var errMsg = 'Token with "' + this.token.type + '" type was not found.'; - if (this.options.silent) { - console.log(errMsg); - } else { - throw new Error(errMsg); - } + + if (typeof opt === 'function') { + callback = opt; + opt = null; } - } -}; -/** - * Slugger generates header id - */ + opt = merge$2({}, marked.defaults, opt || {}); + checkSanitizeDeprecation$1(opt); -function Slugger () { - this.seen = {}; -} + if (callback) { + var highlight = opt.highlight; + var tokens; -/** - * Convert string to unique id - */ + try { + tokens = Lexer_1.lex(src, opt); + } catch (e) { + return callback(e); + } -Slugger.prototype.slug = function (value) { - var slug = value - .toLowerCase() - .trim() - .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '') - .replace(/\s/g, '-'); - - 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; + var done = function done(err) { + var out; - return slug; -}; + if (!err) { + try { + out = Parser_1.parse(tokens, opt); + } catch (e) { + err = e; + } + } -/** - * Helpers - */ + opt.highlight = highlight; + return err ? callback(err) : callback(null, out); + }; -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]; }); - } - } + if (!highlight || highlight.length < 3) { + return done(); + } - return html; -} - -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); + 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; } - }; -} -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; + var _tokens = Lexer_1.lex(src, opt); -function noop() {} -noop.exec = noop; + if (opt.walkTokens) { + marked.walkTokens(_tokens, opt.walkTokens); + } -function merge(obj) { - var i = 1, - target, - key; + return Parser_1.parse(_tokens, opt); + } catch (e) { + e.message += '\nPlease report this to https://github.com/markedjs/marked.'; - for (; i < arguments.length; i++) { - target = arguments[i]; - for (key in target) { - if (Object.prototype.hasOwnProperty.call(target, key)) { - obj[key] = target[key]; + if (opt.silent) { + return '

    An error occurred:

    ' + escape$2(e.message + '', true) + '
    '; } + + throw e; } } + /** + * Options + */ - 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(/ \|/), - 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 ''; - } + marked.options = marked.setOptions = function (opt) { + merge$2(marked.defaults, opt); + changeDefaults(marked.defaults); + return marked; + }; - // Length of suffix matching the invert condition. - var suffLen = 0; + marked.getDefaults = getDefaults; + marked.defaults = defaults$5; + /** + * Use Extension + */ - // 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; - } - } + marked.use = function (extension) { + var opts = merge$2({}, extension); - return str.substr(0, str.length - suffLen); -} + if (extension.renderer) { + (function () { + var renderer = marked.defaults.renderer || new Renderer_1(); -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 -1; -} + var _loop = function _loop(prop) { + var prevRenderer = renderer[prop]; -/** - * Marked - */ + renderer[prop] = function () { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } -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'); - } + var ret = extension.renderer[prop].apply(renderer, args); - if (callback || typeof opt === 'function') { - if (!callback) { - callback = opt; - opt = null; - } + if (ret === false) { + ret = prevRenderer.apply(renderer, args); + } - opt = merge({}, marked.defaults, opt || {}); + return ret; + }; + }; - var highlight = opt.highlight, - tokens, - pending, - i = 0; + for (var prop in extension.renderer) { + _loop(prop); + } - try { - tokens = Lexer.lex(src, opt); - } catch (e) { - return callback(e); + opts.renderer = renderer; + })(); } - pending = tokens.length; + if (extension.tokenizer) { + (function () { + var tokenizer = marked.defaults.tokenizer || new Tokenizer_1(); - var done = function(err) { - if (err) { - opt.highlight = highlight; - return callback(err); - } + var _loop2 = function _loop2(prop) { + var prevTokenizer = tokenizer[prop]; - var out; + tokenizer[prop] = function () { + for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } - try { - out = Parser.parse(tokens, opt); - } catch (e) { - err = e; - } + var ret = extension.tokenizer[prop].apply(tokenizer, args); - opt.highlight = highlight; + if (ret === false) { + ret = prevTokenizer.apply(tokenizer, args); + } - return err - ? callback(err) - : callback(null, out); - }; + return ret; + }; + }; + + for (var prop in extension.tokenizer) { + _loop2(prop); + } - if (!highlight || highlight.length < 3) { - return done(); + opts.tokenizer = tokenizer; + })(); } - delete opt.highlight; + if (extension.walkTokens) { + var walkTokens = marked.defaults.walkTokens; - if (!pending) return done(); + opts.walkTokens = function (token) { + extension.walkTokens(token); - for (; i < tokens.length; i++) { - (function(token) { - if (token.type !== 'code') { - return --pending || done(); + if (walkTokens) { + walkTokens(token); } - return highlight(token.text, token.lang, function(err, code) { - if (err) return done(err); - if (code == null || code === token.text) { - return --pending || done(); - } - token.text = code; - token.escaped = true; - --pending || done(); - }); - })(tokens[i]); + }; } - 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)
    -        + '
    '; - } - throw e; - } -} + 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; + } -/** - * Options - */ + case 'list': + { + marked.walkTokens(token.items, callback); + break; + } -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 + default: + { + if (token.tokens) { + marked.walkTokens(token.tokens, callback); + } + } + } + } }; -}; + /** + * Expose + */ -marked.defaults = marked.getDefaults(); -/** - * 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; -marked.Parser = Parser; -marked.parser = Parser.parse; - -marked.Renderer = Renderer; -marked.TextRenderer = TextRenderer; - -marked.Lexer = Lexer; -marked.lexer = Lexer.lex; - -marked.InlineLexer = InlineLexer; -marked.inlineLexer = InlineLexer.output; - -marked.Slugger = Slugger; - -marked.parse = marked; - -// 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); - -// ESM-comment-begin -define(function() { return __marked_exports; }); -// ESM-comment-end - -// 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 +}))); diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 335ee5691e06c..4af920357fd2d 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 0a3a692a30719..73c8e05549da9 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'; @@ -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; } } @@ -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 0000000000000..ba7feffef573a --- /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 231180d513fb2..4b6aebc16466d 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -12,47 +12,72 @@ export namespace Schemas { * A schema that is used for models that exist in memory * only and that have no correspondence on a server or such. */ - export const inMemory: string = 'inmemory'; + export const inMemory = 'inmemory'; /** * A schema that is used for setting files */ - export const vscode: string = 'vscode'; + export const vscode = 'vscode'; /** * A schema that is used for internal private files */ - export const internal: string = 'private'; + export const internal = 'private'; /** * A walk-through document. */ - export const walkThrough: string = 'walkThrough'; + export const walkThrough = 'walkThrough'; /** * An embedded code snippet. */ - export const walkThroughSnippet: string = 'walkThroughSnippet'; + export const walkThroughSnippet = 'walkThroughSnippet'; - export const http: string = 'http'; + export const http = 'http'; - export const https: string = 'https'; + export const https = 'https'; - export const file: string = 'file'; + export const file = 'file'; - export const mailto: string = 'mailto'; + export const mailto = 'mailto'; - export const untitled: string = 'untitled'; + export const untitled = 'untitled'; - export const data: string = 'data'; + export const data = 'data'; - export const command: string = 'command'; + export const command = 'command'; - export const vscodeRemote: string = 'vscode-remote'; + export const vscodeRemote = 'vscode-remote'; - export const vscodeRemoteResource: string = 'vscode-remote-resource'; + export const vscodeRemoteResource = 'vscode-remote-resource'; - export const userData: string = 'vscode-userdata'; + 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 { diff --git a/src/vs/base/common/normalization.ts b/src/vs/base/common/normalization.ts index b6304df31a032..3a94fb716ec33 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 1c694e1d84a33..9edfddfe5338a 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 1475bf4a5501c..ffb71d9aec7f1 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -113,6 +113,9 @@ export function mixin(destination: any, source: any, overwrite: boolean = true): return destination; } +/** + * @deprecated ES6 + */ 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; @@ -176,18 +179,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; diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 28b7d5e61cc49..d12384b6f16a2 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. @@ -69,11 +69,11 @@ function validateString(value: string, name: string) { } } -function isPathSeparator(code: number) { +function isPathSeparator(code: number | undefined) { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; } -function isPosixPathSeparator(code: number) { +function isPosixPathSeparator(code: number | undefined) { return code === CHAR_FORWARD_SLASH; } @@ -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; + } + } else if (isPathSeparator(code)) { + // Possible UNC root - 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 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) { - 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; - } + // Match 1 or more non-path separators + while (j < len && !isPathSeparator(path.charCodeAt(j))) { + 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; + 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; } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator - rootEnd = 1; - isAbsolute = true; } - 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 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 (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 (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.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.length <= 2) { + return path; + } - 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; + } + 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); + 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 - - 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; - } + // `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 + 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 (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; } - } else if (isPathSeparator(code)) { - // `path` contains just a path separator, exit early to avoid - // unnecessary work - ret.root = ret.dir = path; - return ret; } - 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; + } + 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); + 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 657f250d8b31a..a26c1ebd73162 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 2bc7710862012..5893db444959e 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; @@ -100,9 +88,9 @@ if (typeof global === 'object') { if (typeof define === 'function') { // amd define([], function () { return _factory(sharedObj); }); -} else if (typeof module === "object" && typeof module.exports === "object") { +} else if (typeof module === 'object' && typeof module.exports === 'object') { // 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 b3c7001a51dea..0bbc5d6ef911b 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -10,6 +10,7 @@ let _isMacintosh = false; let _isLinux = false; let _isNative = false; let _isWeb = false; +let _isIOS = false; let _locale: string | undefined = undefined; let _language: string = LANGUAGE_DEFAULT; let _translationsConfigFile: string | undefined = undefined; @@ -41,6 +42,7 @@ declare const global: any; interface INavigator { userAgent: string; language: string; + maxTouchPoints?: number; } declare const navigator: INavigator; declare const self: any; @@ -52,6 +54,7 @@ if (typeof navigator === 'object' && !isElectronRenderer) { _userAgent = navigator.userAgent; _isWindows = _userAgent.indexOf('Windows') >= 0; _isMacintosh = _userAgent.indexOf('Macintosh') >= 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; @@ -106,6 +109,7 @@ export const isMacintosh = _isMacintosh; export const isLinux = _isLinux; export const isNative = _isNative; export const isWeb = _isWeb; +export const isIOS = _isIOS; export const platform = _platform; export const userAgent = _userAgent; @@ -204,4 +208,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/resourceTree.ts b/src/vs/base/common/resourceTree.ts index 4adca4ca4f2d4..23f661e1b80df 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 bea40bbdc628c..621c97368c758 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -5,268 +5,373 @@ 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 originalFSPath(uri: URI): string { + return uriToFsPath(uri, true); } -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; +//#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; } -export function basenameOrAuthority(resource: URI): string { - return basename(resource) || resource.authority; -} +export class ExtUri implements IExtUri { -/** - * 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, '/'); + constructor(private _ignorePathCasing: (uri: URI) => boolean) { } + + compare(uri1: URI, uri2: URI, ignoreFragment: boolean = false): number { + if (uri1 === uri2) { + return 0; } + return strCompare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment)); } - 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; + 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 (!first || !second) { - return false; + getComparisonKey(uri: URI, ignoreFragment: boolean = false): string { + return uri.with({ + path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined, + fragment: ignoreFragment ? null : undefined + }).toString(); } - if (first.scheme !== second.scheme || !isEqualAuthority(first.authority, second.authority)) { + 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); + } + } return false; } - const p1 = first.path || '/', p2 = second.path || '/'; - return p1 === p2 || ignoreCase && equalsIgnoreCase(p1 || '/', p2 || '/'); -} + // --- path math -export function basename(resource: URI): string { - return paths.posix.basename(resource.path); -} + joinPath(resource: URI, ...pathFragment: string[]): URI { + return URI.joinPath(resource, ...pathFragment); + } -export function extname(resource: URI): string { - return paths.posix.extname(resource.path); -} + basenameOrAuthority(resource: URI): string { + return basename(resource) || resource.authority; + } -/** - * 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; + basename(resource: URI): string { + return paths.posix.basename(resource.path); } - if (resource.scheme === Schemas.file) { - return URI.file(paths.dirname(originalFSPath(resource))); + + extname(resource: URI): string { + return paths.posix.extname(resource.path); } - 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 + + 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 + }); } - 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); + 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 + }); } - 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; + 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); } - let normalizedPath: string; - if (resource.scheme === Schemas.file) { - normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path; - } else { - normalizedPath = paths.posix.normalize(resource.path); + + 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) + }); } - 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; + // --- misc + + isAbsolutePath(resource: URI): boolean { + return !!resource.path && resource.path[0] === '/'; } - if (isWindows) { - value = value.replace(/\//g, '\\'); + + isEqualAuthority(a1: string, a2: string) { + return a1 === a2 || equalsIgnoreCase(a1, a2); } - return value; -} -/** - * Returns true if the URI path is absolute. - */ -export function isAbsolutePath(resource: URI): boolean { - return !!resource.path && resource.path[0] === '/'; -} + 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 + } + } -/** - * 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 + 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; } -} -/** - * 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) }); + 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; } - return resource; } + /** - * 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\ + * 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 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; - } - if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) { - return resource.with({ path: resource.path + '/' }); - } - return resource; -} +export const extUri = new ExtUri(() => false); /** - * 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. + * 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 function relativePath(from: URI, to: URI, ignoreCase = hasToIgnoreCase(from)): string | undefined { - if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { - return undefined; - } - if (from.scheme === Schemas.file) { - const relativePath = paths.relative(from.path, to.path); - return isWindows ? extpath.toSlashes(relativePath) : relativePath; - } - 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; - } - } - } - fromPath = toPath.substr(0, i) + fromPath.substr(i); - } - return paths.posix.relative(fromPath, toPath); -} +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; +}); + /** - * Resolves an absolute or relative path against a base URI. - * The path can be relative or absolute posix or a Windows path + * 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 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 - }); - } - 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) - }); -} +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[] = []; @@ -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 faee10bed5695..cb3fe20dc7904 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 7b57f1e5a6624..2eec434e9a796 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/sequence.ts b/src/vs/base/common/sequence.ts index 418d3907c99b6..dc47fe8c095e9 100644 --- a/src/vs/base/common/sequence.ts +++ b/src/vs/base/common/sequence.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; export interface ISplice { 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 602df43e59823..0937e4ddd19c0 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 0000000000000..6b3bd79ef4cee --- /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 1172496a89713..260e13f02b300 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 02fe7deb96066..ec727797b8787 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -14,7 +14,7 @@ export function isFalsyOrWhitespace(str: string | undefined): boolean { } /** - * @returns the provided number with the given number of preceding zeros. + * @deprecated ES6: use `String.padStart` */ export function pad(n: number, l: number, char: string = '0'): string { const str = '' + n; @@ -145,7 +145,7 @@ export function stripWildcards(pattern: string): string { } /** - * Determines if haystack starts with needle. + * @deprecated ES6: use `String.startsWith` */ export function startsWith(haystack: string, needle: string): boolean { if (haystack.length < needle.length) { @@ -166,7 +166,7 @@ export function startsWith(haystack: string, needle: string): boolean { } /** - * Determines if haystack ends with needle. + * @deprecated ES6: use `String.endsWith` */ export function endsWith(haystack: string, needle: string): boolean { const diff = haystack.length - needle.length; @@ -240,7 +240,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 +295,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 === 32 && isUpperAsciiLetter(codeB)) { //codeB =[65-90] && codeA =[97-122] + continue; - if (diff === 0) { - // equal -> ignoreCase + } else if (diff === -32 && isUpperAsciiLetter(codeA)) { //codeB =[97-122] && codeA =[65-90] continue; + } - } else if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) { + 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 +450,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 +479,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,67 +493,87 @@ 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; } export function nextCharLength(str: string, offset: number): number { + const graphemeBreakTree = GraphemeBreakTree.getInstance(); const initialOffset = offset; const len = str.length; - let codePoint = getNextCodePoint(str, len, offset); - offset += (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + const initialCodePoint = getNextCodePoint(str, len, offset); + offset += (initialCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + let graphemeBreakType = graphemeBreakTree.getGraphemeBreakType(initialCodePoint); while (offset < len) { - codePoint = getNextCodePoint(str, len, offset); - if (!isUnicodeMark(codePoint)) { + const nextCodePoint = getNextCodePoint(str, len, offset); + const nextGraphemeBreakType = graphemeBreakTree.getGraphemeBreakType(nextCodePoint); + if (breakBetweenGraphemeBreakType(graphemeBreakType, nextGraphemeBreakType)) { break; } - offset += (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + offset += (nextCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + graphemeBreakType = nextGraphemeBreakType; } return (offset - initialOffset); } export function prevCharLength(str: string, offset: number): number { + const graphemeBreakTree = GraphemeBreakTree.getInstance(); const initialOffset = offset; - let codePoint = getPrevCodePoint(str, offset); - offset -= (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + const initialCodePoint = getPrevCodePoint(str, offset); + offset -= (initialCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); - while (offset > 0 && isUnicodeMark(codePoint)) { - codePoint = getPrevCodePoint(str, offset); - offset -= (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + let graphemeBreakType = graphemeBreakTree.getGraphemeBreakType(initialCodePoint); + while (offset > 0) { + const prevCodePoint = getPrevCodePoint(str, offset); + const prevGraphemeBreakType = graphemeBreakTree.getGraphemeBreakType(prevCodePoint); + if (breakBetweenGraphemeBreakType(prevGraphemeBreakType, graphemeBreakType)) { + break; + } + offset -= (prevCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + graphemeBreakType = prevGraphemeBreakType; } return (initialOffset - offset); } function _getCharContainingOffset(str: string, offset: number): [number, number] { + const graphemeBreakTree = GraphemeBreakTree.getInstance(); const len = str.length; const initialOffset = offset; const initialCodePoint = getNextCodePoint(str, len, offset); + const initialGraphemeBreakType = graphemeBreakTree.getGraphemeBreakType(initialCodePoint); offset += (initialCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); // extend to the right + let graphemeBreakType = initialGraphemeBreakType; while (offset < len) { const nextCodePoint = getNextCodePoint(str, len, offset); - if (!isUnicodeMark(nextCodePoint)) { + const nextGraphemeBreakType = graphemeBreakTree.getGraphemeBreakType(nextCodePoint); + if (breakBetweenGraphemeBreakType(graphemeBreakType, nextGraphemeBreakType)) { break; } offset += (nextCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + graphemeBreakType = nextGraphemeBreakType; } const endOffset = offset; // extend to the left offset = initialOffset; - let codePoint = initialCodePoint; - - while (offset > 0 && isUnicodeMark(codePoint)) { - codePoint = getPrevCodePoint(str, offset); - offset -= (codePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + graphemeBreakType = initialGraphemeBreakType; + while (offset > 0) { + const prevCodePoint = getPrevCodePoint(str, offset); + const prevGraphemeBreakType = graphemeBreakTree.getGraphemeBreakType(prevCodePoint); + if (breakBetweenGraphemeBreakType(prevGraphemeBreakType, graphemeBreakType)) { + break; + } + offset -= (prevCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + graphemeBreakType = prevGraphemeBreakType; } return [offset, endOffset]; @@ -583,95 +586,6 @@ export function getCharContainingOffset(str: string, offset: number): [number, n return _getCharContainingOffset(str, offset); } -export function isUnicodeMark(codePoint: number): boolean { - return MarkClassifier.getInstance().isUnicodeMark(codePoint); -} - -class MarkClassifier { - - private static _INSTANCE: MarkClassifier | null = null; - - public static getInstance(): MarkClassifier { - if (!MarkClassifier._INSTANCE) { - MarkClassifier._INSTANCE = new MarkClassifier(); - } - return MarkClassifier._INSTANCE; - } - - private arr: Uint8Array; - - constructor() { - // generated using https://github.com/alexandrudima/unicode-utils/blob/master/generate-mark-test.js - const ranges = [ - 0x0300, 0x036F, 0x0483, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, - 0x05C7, 0x05C7, 0x0610, 0x061A, 0x064B, 0x065F, 0x0670, 0x0670, 0x06D6, 0x06DC, 0x06DF, 0x06E4, - 0x06E7, 0x06E8, 0x06EA, 0x06ED, 0x0711, 0x0711, 0x0730, 0x074A, 0x07A6, 0x07B0, 0x07EB, 0x07F3, - 0x07FD, 0x07FD, 0x0816, 0x0819, 0x081B, 0x0823, 0x0825, 0x0827, 0x0829, 0x082D, 0x0859, 0x085B, - 0x08D3, 0x08E1, 0x08E3, 0x0903, 0x093A, 0x093C, 0x093E, 0x094F, 0x0951, 0x0957, 0x0962, 0x0963, - 0x0981, 0x0983, 0x09BC, 0x09BC, 0x09BE, 0x09CD, 0x09D7, 0x09D7, 0x09E2, 0x09E3, 0x09FE, 0x0A03, - 0x0A3C, 0x0A51, 0x0A70, 0x0A71, 0x0A75, 0x0A75, 0x0A81, 0x0A83, 0x0ABC, 0x0ABC, 0x0ABE, 0x0ACD, - 0x0AE2, 0x0AE3, 0x0AFA, 0x0B03, 0x0B3C, 0x0B3C, 0x0B3E, 0x0B57, 0x0B62, 0x0B63, 0x0B82, 0x0B82, - 0x0BBE, 0x0BCD, 0x0BD7, 0x0BD7, 0x0C00, 0x0C04, 0x0C3E, 0x0C56, 0x0C62, 0x0C63, 0x0C81, 0x0C83, - 0x0CBC, 0x0CBC, 0x0CBE, 0x0CD6, 0x0CE2, 0x0CE3, 0x0D00, 0x0D03, 0x0D3B, 0x0D3C, 0x0D3E, 0x0D4D, - 0x0D57, 0x0D57, 0x0D62, 0x0D63, 0x0D81, 0x0D83, 0x0DCA, 0x0DDF, 0x0DF2, 0x0DF3, 0x0E31, 0x0E31, - 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1, 0x0EB4, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19, - 0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F3E, 0x0F3F, 0x0F71, 0x0F84, 0x0F86, 0x0F87, - 0x0F8D, 0x0FBC, 0x0FC6, 0x0FC6, 0x102B, 0x103E, 0x1056, 0x1059, 0x105E, 0x1060, 0x1062, 0x1064, - 0x1067, 0x106D, 0x1071, 0x1074, 0x1082, 0x108D, 0x108F, 0x108F, 0x109A, 0x109D, 0x135D, 0x135F, - 0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773, 0x17B4, 0x17D3, 0x17DD, 0x17DD, - 0x180B, 0x180D, 0x1885, 0x1886, 0x18A9, 0x18A9, 0x1920, 0x193B, 0x1A17, 0x1A1B, 0x1A55, 0x1A7F, - 0x1AB0, 0x1B04, 0x1B34, 0x1B44, 0x1B6B, 0x1B73, 0x1B80, 0x1B82, 0x1BA1, 0x1BAD, 0x1BE6, 0x1BF3, - 0x1C24, 0x1C37, 0x1CD0, 0x1CD2, 0x1CD4, 0x1CE8, 0x1CED, 0x1CED, 0x1CF4, 0x1CF4, 0x1CF7, 0x1CF9, - 0x1DC0, 0x1DFF, 0x20D0, 0x20F0, 0x2CEF, 0x2CF1, 0x2D7F, 0x2D7F, 0x2DE0, 0x2DFF, 0x302A, 0x302F, - 0x3099, 0x309A, 0xA66F, 0xA672, 0xA674, 0xA67D, 0xA69E, 0xA69F, 0xA6F0, 0xA6F1, 0xA802, 0xA802, - 0xA806, 0xA806, 0xA80B, 0xA80B, 0xA823, 0xA827, 0xA82C, 0xA82C, 0xA880, 0xA881, 0xA8B4, 0xA8C5, - 0xA8E0, 0xA8F1, 0xA8FF, 0xA8FF, 0xA926, 0xA92D, 0xA947, 0xA953, 0xA980, 0xA983, 0xA9B3, 0xA9C0, - 0xA9E5, 0xA9E5, 0xAA29, 0xAA36, 0xAA43, 0xAA43, 0xAA4C, 0xAA4D, 0xAA7B, 0xAA7D, 0xAAB0, 0xAAB0, - 0xAAB2, 0xAAB4, 0xAAB7, 0xAAB8, 0xAABE, 0xAABF, 0xAAC1, 0xAAC1, 0xAAEB, 0xAAEF, 0xAAF5, 0xAAF6, - 0xABE3, 0xABEA, 0xABEC, 0xABED, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F, 0xFE20, 0xFE2F, 0x101FD, 0x101FD, - 0x102E0, 0x102E0, 0x10376, 0x1037A, 0x10A01, 0x10A0F, 0x10A38, 0x10A3F, 0x10AE5, 0x10AE6, 0x10D24, 0x10D27, - 0x10EAB, 0x10EAC, 0x10F46, 0x10F50, 0x11000, 0x11002, 0x11038, 0x11046, 0x1107F, 0x11082, 0x110B0, 0x110BA, - 0x11100, 0x11102, 0x11127, 0x11134, 0x11145, 0x11146, 0x11173, 0x11173, 0x11180, 0x11182, 0x111B3, 0x111C0, - 0x111C9, 0x111CC, 0x111CE, 0x111CF, 0x1122C, 0x11237, 0x1123E, 0x1123E, 0x112DF, 0x112EA, 0x11300, 0x11303, - 0x1133B, 0x1133C, 0x1133E, 0x1134D, 0x11357, 0x11357, 0x11362, 0x11374, 0x11435, 0x11446, 0x1145E, 0x1145E, - 0x114B0, 0x114C3, 0x115AF, 0x115C0, 0x115DC, 0x115DD, 0x11630, 0x11640, 0x116AB, 0x116B7, 0x1171D, 0x1172B, - 0x1182C, 0x1183A, 0x11930, 0x1193E, 0x11940, 0x11940, 0x11942, 0x11943, 0x119D1, 0x119E0, 0x119E4, 0x119E4, - 0x11A01, 0x11A0A, 0x11A33, 0x11A39, 0x11A3B, 0x11A3E, 0x11A47, 0x11A47, 0x11A51, 0x11A5B, 0x11A8A, 0x11A99, - 0x11C2F, 0x11C3F, 0x11C92, 0x11CB6, 0x11D31, 0x11D45, 0x11D47, 0x11D47, 0x11D8A, 0x11D97, 0x11EF3, 0x11EF6, - 0x16AF0, 0x16AF4, 0x16B30, 0x16B36, 0x16F4F, 0x16F4F, 0x16F51, 0x16F92, 0x16FE4, 0x16FF1, 0x1BC9D, 0x1BC9E, - 0x1D165, 0x1D169, 0x1D16D, 0x1D172, 0x1D17B, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD, 0x1D242, 0x1D244, - 0x1DA00, 0x1DA36, 0x1DA3B, 0x1DA6C, 0x1DA75, 0x1DA75, 0x1DA84, 0x1DA84, 0x1DA9B, 0x1E02A, 0x1E130, 0x1E136, - 0x1E2EC, 0x1E2EF, 0x1E8D0, 0x1E8D6, 0x1E944, 0x1E94A, 0xE0100, 0xE01EF - ]; - - const maxCodePoint = ranges[ranges.length - 1]; - const arrLen = Math.ceil(maxCodePoint / 8); - const arr = new Uint8Array(arrLen); - - for (let i = 0, len = ranges.length / 2; i < len; i++) { - const from = ranges[2 * i]; - const to = ranges[2 * i + 1]; - - for (let j = from; j <= to; j++) { - const div8 = j >>> 3; - const mod8 = j & 7; - arr[div8] = arr[div8] | (1 << mod8); - } - } - - this.arr = arr; - } - - public isUnicodeMark(codePoint: number): boolean { - const div8 = codePoint >>> 3; - const mod8 = codePoint & 7; - if (div8 >= this.arr.length) { - return false; - } - return (this.arr[div8] & (1 << mod8)) ? true : false; - } -} - /** * A manual encoding of `str` to UTF8. * Use only in environments which do not offer native conversion methods! @@ -814,6 +728,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))) { @@ -921,21 +843,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); @@ -948,10 +855,9 @@ 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 -} - +/** + * @deprecated ES6 + */ export function repeat(s: string, count: number): string { let result = ''; for (let i = 0; i < count; i++) { @@ -1038,3 +944,167 @@ export function singleLetterHash(n: number): string { return String.fromCharCode(CharCode.A + n - LETTERS_CNT); } + +//#region Unicode Grapheme Break + +export function getGraphemeBreakType(codePoint: number): GraphemeBreakType { + const graphemeBreakTree = GraphemeBreakTree.getInstance(); + return graphemeBreakTree.getGraphemeBreakType(codePoint); +} + +export function breakBetweenGraphemeBreakType(breakTypeA: GraphemeBreakType, breakTypeB: GraphemeBreakType): boolean { + // http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules + + // !!! Let's make the common case a bit faster + if (breakTypeA === GraphemeBreakType.Other) { + // see https://www.unicode.org/Public/13.0.0/ucd/auxiliary/GraphemeBreakTest-13.0.0d10.html#table + return (breakTypeB !== GraphemeBreakType.Extend && breakTypeB !== GraphemeBreakType.SpacingMark); + } + + // Do not break between a CR and LF. Otherwise, break before and after controls. + // GB3 CR × LF + // GB4 (Control | CR | LF) ÷ + // GB5 ÷ (Control | CR | LF) + if (breakTypeA === GraphemeBreakType.CR) { + if (breakTypeB === GraphemeBreakType.LF) { + return false; // GB3 + } + } + if (breakTypeA === GraphemeBreakType.Control || breakTypeA === GraphemeBreakType.CR || breakTypeA === GraphemeBreakType.LF) { + return true; // GB4 + } + if (breakTypeB === GraphemeBreakType.Control || breakTypeB === GraphemeBreakType.CR || breakTypeB === GraphemeBreakType.LF) { + return true; // GB5 + } + + // Do not break Hangul syllable sequences. + // GB6 L × (L | V | LV | LVT) + // GB7 (LV | V) × (V | T) + // GB8 (LVT | T) × T + if (breakTypeA === GraphemeBreakType.L) { + if (breakTypeB === GraphemeBreakType.L || breakTypeB === GraphemeBreakType.V || breakTypeB === GraphemeBreakType.LV || breakTypeB === GraphemeBreakType.LVT) { + return false; // GB6 + } + } + if (breakTypeA === GraphemeBreakType.LV || breakTypeA === GraphemeBreakType.V) { + if (breakTypeB === GraphemeBreakType.V || breakTypeB === GraphemeBreakType.T) { + return false; // GB7 + } + } + if (breakTypeA === GraphemeBreakType.LVT || breakTypeA === GraphemeBreakType.T) { + if (breakTypeB === GraphemeBreakType.T) { + return false; // GB8 + } + } + + // Do not break before extending characters or ZWJ. + // GB9 × (Extend | ZWJ) + if (breakTypeB === GraphemeBreakType.Extend || breakTypeB === GraphemeBreakType.ZWJ) { + return false; // GB9 + } + + // The GB9a and GB9b rules only apply to extended grapheme clusters: + // Do not break before SpacingMarks, or after Prepend characters. + // GB9a × SpacingMark + // GB9b Prepend × + if (breakTypeB === GraphemeBreakType.SpacingMark) { + return false; // GB9a + } + if (breakTypeA === GraphemeBreakType.Prepend) { + return false; // GB9b + } + + // Do not break within emoji modifier sequences or emoji zwj sequences. + // GB11 \p{Extended_Pictographic} Extend* ZWJ × \p{Extended_Pictographic} + if (breakTypeA === GraphemeBreakType.ZWJ && breakTypeB === GraphemeBreakType.Extended_Pictographic) { + // Note: we are not implementing the rule entirely here to avoid introducing states + return false; // GB11 + } + + // GB12 sot (RI RI)* RI × RI + // GB13 [^RI] (RI RI)* RI × RI + if (breakTypeA === GraphemeBreakType.Regional_Indicator && breakTypeB === GraphemeBreakType.Regional_Indicator) { + // Note: we are not implementing the rule entirely here to avoid introducing states + return false; // GB12 & GB13 + } + + // GB999 Any ÷ Any + return true; +} + +export const enum GraphemeBreakType { + Other = 0, + Prepend = 1, + CR = 2, + LF = 3, + Control = 4, + Extend = 5, + Regional_Indicator = 6, + SpacingMark = 7, + L = 8, + V = 9, + T = 10, + LV = 11, + LVT = 12, + ZWJ = 13, + Extended_Pictographic = 14 +} + +class GraphemeBreakTree { + + private static _INSTANCE: GraphemeBreakTree | null = null; + public static getInstance(): GraphemeBreakTree { + if (!GraphemeBreakTree._INSTANCE) { + GraphemeBreakTree._INSTANCE = new GraphemeBreakTree(); + } + return GraphemeBreakTree._INSTANCE; + } + + private readonly _data: number[]; + + constructor() { + this._data = getGraphemeBreakRawData(); + } + + public getGraphemeBreakType(codePoint: number): GraphemeBreakType { + // !!! Let's make 7bit ASCII a bit faster: 0..31 + if (codePoint < 32) { + if (codePoint === CharCode.LineFeed) { + return GraphemeBreakType.LF; + } + if (codePoint === CharCode.CarriageReturn) { + return GraphemeBreakType.CR; + } + return GraphemeBreakType.Control; + } + // !!! Let's make 7bit ASCII a bit faster: 32..126 + if (codePoint < 127) { + return GraphemeBreakType.Other; + } + + const data = this._data; + const nodeCount = data.length / 3; + let nodeIndex = 1; + while (nodeIndex <= nodeCount) { + if (codePoint < data[3 * nodeIndex]) { + // go left + nodeIndex = 2 * nodeIndex; + } else if (codePoint > data[3 * nodeIndex + 1]) { + // go right + nodeIndex = 2 * nodeIndex + 1; + } else { + // hit + return data[3 * nodeIndex + 2]; + } + } + + return GraphemeBreakType.Other; + } +} + +function getGraphemeBreakRawData(): number[] { + // generated using https://github.com/alexandrudima/unicode-utils/blob/master/generate-grapheme-break.js + return JSON.parse('[0,0,0,51592,51592,11,44424,44424,11,72251,72254,5,7150,7150,7,48008,48008,11,55176,55176,11,128420,128420,14,3276,3277,5,9979,9980,14,46216,46216,11,49800,49800,11,53384,53384,11,70726,70726,5,122915,122916,5,129320,129327,14,2558,2558,5,5906,5908,5,9762,9763,14,43360,43388,8,45320,45320,11,47112,47112,11,48904,48904,11,50696,50696,11,52488,52488,11,54280,54280,11,70082,70083,1,71350,71350,7,73111,73111,5,127892,127893,14,128726,128727,14,129473,129474,14,2027,2035,5,2901,2902,5,3784,3789,5,6754,6754,5,8418,8420,5,9877,9877,14,11088,11088,14,44008,44008,5,44872,44872,11,45768,45768,11,46664,46664,11,47560,47560,11,48456,48456,11,49352,49352,11,50248,50248,11,51144,51144,11,52040,52040,11,52936,52936,11,53832,53832,11,54728,54728,11,69811,69814,5,70459,70460,5,71096,71099,7,71998,71998,5,72874,72880,5,119149,119149,7,127374,127374,14,128335,128335,14,128482,128482,14,128765,128767,14,129399,129400,14,129680,129685,14,1476,1477,5,2377,2380,7,2759,2760,5,3137,3140,7,3458,3459,7,4153,4154,5,6432,6434,5,6978,6978,5,7675,7679,5,9723,9726,14,9823,9823,14,9919,9923,14,10035,10036,14,42736,42737,5,43596,43596,5,44200,44200,11,44648,44648,11,45096,45096,11,45544,45544,11,45992,45992,11,46440,46440,11,46888,46888,11,47336,47336,11,47784,47784,11,48232,48232,11,48680,48680,11,49128,49128,11,49576,49576,11,50024,50024,11,50472,50472,11,50920,50920,11,51368,51368,11,51816,51816,11,52264,52264,11,52712,52712,11,53160,53160,11,53608,53608,11,54056,54056,11,54504,54504,11,54952,54952,11,68108,68111,5,69933,69940,5,70197,70197,7,70498,70499,7,70845,70845,5,71229,71229,5,71727,71735,5,72154,72155,5,72344,72345,5,73023,73029,5,94095,94098,5,121403,121452,5,126981,127182,14,127538,127546,14,127990,127990,14,128391,128391,14,128445,128449,14,128500,128505,14,128752,128752,14,129160,129167,14,129356,129356,14,129432,129442,14,129648,129651,14,129751,131069,14,173,173,4,1757,1757,1,2274,2274,1,2494,2494,5,2641,2641,5,2876,2876,5,3014,3016,7,3262,3262,7,3393,3396,5,3570,3571,7,3968,3972,5,4228,4228,7,6086,6086,5,6679,6680,5,6912,6915,5,7080,7081,5,7380,7392,5,8252,8252,14,9096,9096,14,9748,9749,14,9784,9786,14,9833,9850,14,9890,9894,14,9938,9938,14,9999,9999,14,10085,10087,14,12349,12349,14,43136,43137,7,43454,43456,7,43755,43755,7,44088,44088,11,44312,44312,11,44536,44536,11,44760,44760,11,44984,44984,11,45208,45208,11,45432,45432,11,45656,45656,11,45880,45880,11,46104,46104,11,46328,46328,11,46552,46552,11,46776,46776,11,47000,47000,11,47224,47224,11,47448,47448,11,47672,47672,11,47896,47896,11,48120,48120,11,48344,48344,11,48568,48568,11,48792,48792,11,49016,49016,11,49240,49240,11,49464,49464,11,49688,49688,11,49912,49912,11,50136,50136,11,50360,50360,11,50584,50584,11,50808,50808,11,51032,51032,11,51256,51256,11,51480,51480,11,51704,51704,11,51928,51928,11,52152,52152,11,52376,52376,11,52600,52600,11,52824,52824,11,53048,53048,11,53272,53272,11,53496,53496,11,53720,53720,11,53944,53944,11,54168,54168,11,54392,54392,11,54616,54616,11,54840,54840,11,55064,55064,11,65438,65439,5,69633,69633,5,69837,69837,1,70018,70018,7,70188,70190,7,70368,70370,7,70465,70468,7,70712,70719,5,70835,70840,5,70850,70851,5,71132,71133,5,71340,71340,7,71458,71461,5,71985,71989,7,72002,72002,7,72193,72202,5,72281,72283,5,72766,72766,7,72885,72886,5,73104,73105,5,92912,92916,5,113824,113827,4,119173,119179,5,121505,121519,5,125136,125142,5,127279,127279,14,127489,127490,14,127570,127743,14,127900,127901,14,128254,128254,14,128369,128370,14,128400,128400,14,128425,128432,14,128468,128475,14,128489,128494,14,128715,128720,14,128745,128745,14,128759,128760,14,129004,129023,14,129296,129304,14,129340,129342,14,129388,129392,14,129404,129407,14,129454,129455,14,129485,129487,14,129659,129663,14,129719,129727,14,917536,917631,5,13,13,2,1160,1161,5,1564,1564,4,1807,1807,1,2085,2087,5,2363,2363,7,2402,2403,5,2507,2508,7,2622,2624,7,2691,2691,7,2786,2787,5,2881,2884,5,3006,3006,5,3072,3072,5,3170,3171,5,3267,3268,7,3330,3331,7,3406,3406,1,3538,3540,5,3655,3662,5,3897,3897,5,4038,4038,5,4184,4185,5,4352,4447,8,6068,6069,5,6155,6157,5,6448,6449,7,6742,6742,5,6783,6783,5,6966,6970,5,7042,7042,7,7143,7143,7,7212,7219,5,7412,7412,5,8206,8207,4,8294,8303,4,8596,8601,14,9410,9410,14,9742,9742,14,9757,9757,14,9770,9770,14,9794,9794,14,9828,9828,14,9855,9855,14,9882,9882,14,9900,9903,14,9929,9933,14,9963,9967,14,9987,9988,14,10006,10006,14,10062,10062,14,10175,10175,14,11744,11775,5,42607,42607,5,43043,43044,7,43263,43263,5,43444,43445,7,43569,43570,5,43698,43700,5,43766,43766,5,44032,44032,11,44144,44144,11,44256,44256,11,44368,44368,11,44480,44480,11,44592,44592,11,44704,44704,11,44816,44816,11,44928,44928,11,45040,45040,11,45152,45152,11,45264,45264,11,45376,45376,11,45488,45488,11,45600,45600,11,45712,45712,11,45824,45824,11,45936,45936,11,46048,46048,11,46160,46160,11,46272,46272,11,46384,46384,11,46496,46496,11,46608,46608,11,46720,46720,11,46832,46832,11,46944,46944,11,47056,47056,11,47168,47168,11,47280,47280,11,47392,47392,11,47504,47504,11,47616,47616,11,47728,47728,11,47840,47840,11,47952,47952,11,48064,48064,11,48176,48176,11,48288,48288,11,48400,48400,11,48512,48512,11,48624,48624,11,48736,48736,11,48848,48848,11,48960,48960,11,49072,49072,11,49184,49184,11,49296,49296,11,49408,49408,11,49520,49520,11,49632,49632,11,49744,49744,11,49856,49856,11,49968,49968,11,50080,50080,11,50192,50192,11,50304,50304,11,50416,50416,11,50528,50528,11,50640,50640,11,50752,50752,11,50864,50864,11,50976,50976,11,51088,51088,11,51200,51200,11,51312,51312,11,51424,51424,11,51536,51536,11,51648,51648,11,51760,51760,11,51872,51872,11,51984,51984,11,52096,52096,11,52208,52208,11,52320,52320,11,52432,52432,11,52544,52544,11,52656,52656,11,52768,52768,11,52880,52880,11,52992,52992,11,53104,53104,11,53216,53216,11,53328,53328,11,53440,53440,11,53552,53552,11,53664,53664,11,53776,53776,11,53888,53888,11,54000,54000,11,54112,54112,11,54224,54224,11,54336,54336,11,54448,54448,11,54560,54560,11,54672,54672,11,54784,54784,11,54896,54896,11,55008,55008,11,55120,55120,11,64286,64286,5,66272,66272,5,68900,68903,5,69762,69762,7,69817,69818,5,69927,69931,5,70003,70003,5,70070,70078,5,70094,70094,7,70194,70195,7,70206,70206,5,70400,70401,5,70463,70463,7,70475,70477,7,70512,70516,5,70722,70724,5,70832,70832,5,70842,70842,5,70847,70848,5,71088,71089,7,71102,71102,7,71219,71226,5,71231,71232,5,71342,71343,7,71453,71455,5,71463,71467,5,71737,71738,5,71995,71996,5,72000,72000,7,72145,72147,7,72160,72160,5,72249,72249,7,72273,72278,5,72330,72342,5,72752,72758,5,72850,72871,5,72882,72883,5,73018,73018,5,73031,73031,5,73109,73109,5,73461,73462,7,94031,94031,5,94192,94193,7,119142,119142,7,119155,119162,4,119362,119364,5,121476,121476,5,122888,122904,5,123184,123190,5,126976,126979,14,127184,127231,14,127344,127345,14,127405,127461,14,127514,127514,14,127561,127567,14,127778,127779,14,127896,127896,14,127985,127986,14,127995,127999,5,128326,128328,14,128360,128366,14,128378,128378,14,128394,128397,14,128405,128406,14,128422,128423,14,128435,128443,14,128453,128464,14,128479,128480,14,128484,128487,14,128496,128498,14,128640,128709,14,128723,128724,14,128736,128741,14,128747,128748,14,128755,128755,14,128762,128762,14,128981,128991,14,129096,129103,14,129292,129292,14,129311,129311,14,129329,129330,14,129344,129349,14,129360,129374,14,129394,129394,14,129402,129402,14,129413,129425,14,129445,129450,14,129466,129471,14,129483,129483,14,129511,129535,14,129653,129655,14,129667,129670,14,129705,129711,14,129731,129743,14,917505,917505,4,917760,917999,5,10,10,3,127,159,4,768,879,5,1471,1471,5,1536,1541,1,1648,1648,5,1767,1768,5,1840,1866,5,2070,2073,5,2137,2139,5,2307,2307,7,2366,2368,7,2382,2383,7,2434,2435,7,2497,2500,5,2519,2519,5,2563,2563,7,2631,2632,5,2677,2677,5,2750,2752,7,2763,2764,7,2817,2817,5,2879,2879,5,2891,2892,7,2914,2915,5,3008,3008,5,3021,3021,5,3076,3076,5,3146,3149,5,3202,3203,7,3264,3265,7,3271,3272,7,3298,3299,5,3390,3390,5,3402,3404,7,3426,3427,5,3535,3535,5,3544,3550,7,3635,3635,7,3763,3763,7,3893,3893,5,3953,3966,5,3981,3991,5,4145,4145,7,4157,4158,5,4209,4212,5,4237,4237,5,4520,4607,10,5970,5971,5,6071,6077,5,6089,6099,5,6277,6278,5,6439,6440,5,6451,6456,7,6683,6683,5,6744,6750,5,6765,6770,7,6846,6846,5,6964,6964,5,6972,6972,5,7019,7027,5,7074,7077,5,7083,7085,5,7146,7148,7,7154,7155,7,7222,7223,5,7394,7400,5,7416,7417,5,8204,8204,5,8233,8233,4,8288,8292,4,8413,8416,5,8482,8482,14,8986,8987,14,9193,9203,14,9654,9654,14,9733,9733,14,9745,9745,14,9752,9752,14,9760,9760,14,9766,9766,14,9774,9775,14,9792,9792,14,9800,9811,14,9825,9826,14,9831,9831,14,9852,9853,14,9872,9873,14,9880,9880,14,9885,9887,14,9896,9897,14,9906,9916,14,9926,9927,14,9936,9936,14,9941,9960,14,9974,9974,14,9982,9985,14,9992,9997,14,10002,10002,14,10017,10017,14,10055,10055,14,10071,10071,14,10145,10145,14,11013,11015,14,11503,11505,5,12334,12335,5,12951,12951,14,42612,42621,5,43014,43014,5,43047,43047,7,43204,43205,5,43335,43345,5,43395,43395,7,43450,43451,7,43561,43566,5,43573,43574,5,43644,43644,5,43710,43711,5,43758,43759,7,44005,44005,5,44012,44012,7,44060,44060,11,44116,44116,11,44172,44172,11,44228,44228,11,44284,44284,11,44340,44340,11,44396,44396,11,44452,44452,11,44508,44508,11,44564,44564,11,44620,44620,11,44676,44676,11,44732,44732,11,44788,44788,11,44844,44844,11,44900,44900,11,44956,44956,11,45012,45012,11,45068,45068,11,45124,45124,11,45180,45180,11,45236,45236,11,45292,45292,11,45348,45348,11,45404,45404,11,45460,45460,11,45516,45516,11,45572,45572,11,45628,45628,11,45684,45684,11,45740,45740,11,45796,45796,11,45852,45852,11,45908,45908,11,45964,45964,11,46020,46020,11,46076,46076,11,46132,46132,11,46188,46188,11,46244,46244,11,46300,46300,11,46356,46356,11,46412,46412,11,46468,46468,11,46524,46524,11,46580,46580,11,46636,46636,11,46692,46692,11,46748,46748,11,46804,46804,11,46860,46860,11,46916,46916,11,46972,46972,11,47028,47028,11,47084,47084,11,47140,47140,11,47196,47196,11,47252,47252,11,47308,47308,11,47364,47364,11,47420,47420,11,47476,47476,11,47532,47532,11,47588,47588,11,47644,47644,11,47700,47700,11,47756,47756,11,47812,47812,11,47868,47868,11,47924,47924,11,47980,47980,11,48036,48036,11,48092,48092,11,48148,48148,11,48204,48204,11,48260,48260,11,48316,48316,11,48372,48372,11,48428,48428,11,48484,48484,11,48540,48540,11,48596,48596,11,48652,48652,11,48708,48708,11,48764,48764,11,48820,48820,11,48876,48876,11,48932,48932,11,48988,48988,11,49044,49044,11,49100,49100,11,49156,49156,11,49212,49212,11,49268,49268,11,49324,49324,11,49380,49380,11,49436,49436,11,49492,49492,11,49548,49548,11,49604,49604,11,49660,49660,11,49716,49716,11,49772,49772,11,49828,49828,11,49884,49884,11,49940,49940,11,49996,49996,11,50052,50052,11,50108,50108,11,50164,50164,11,50220,50220,11,50276,50276,11,50332,50332,11,50388,50388,11,50444,50444,11,50500,50500,11,50556,50556,11,50612,50612,11,50668,50668,11,50724,50724,11,50780,50780,11,50836,50836,11,50892,50892,11,50948,50948,11,51004,51004,11,51060,51060,11,51116,51116,11,51172,51172,11,51228,51228,11,51284,51284,11,51340,51340,11,51396,51396,11,51452,51452,11,51508,51508,11,51564,51564,11,51620,51620,11,51676,51676,11,51732,51732,11,51788,51788,11,51844,51844,11,51900,51900,11,51956,51956,11,52012,52012,11,52068,52068,11,52124,52124,11,52180,52180,11,52236,52236,11,52292,52292,11,52348,52348,11,52404,52404,11,52460,52460,11,52516,52516,11,52572,52572,11,52628,52628,11,52684,52684,11,52740,52740,11,52796,52796,11,52852,52852,11,52908,52908,11,52964,52964,11,53020,53020,11,53076,53076,11,53132,53132,11,53188,53188,11,53244,53244,11,53300,53300,11,53356,53356,11,53412,53412,11,53468,53468,11,53524,53524,11,53580,53580,11,53636,53636,11,53692,53692,11,53748,53748,11,53804,53804,11,53860,53860,11,53916,53916,11,53972,53972,11,54028,54028,11,54084,54084,11,54140,54140,11,54196,54196,11,54252,54252,11,54308,54308,11,54364,54364,11,54420,54420,11,54476,54476,11,54532,54532,11,54588,54588,11,54644,54644,11,54700,54700,11,54756,54756,11,54812,54812,11,54868,54868,11,54924,54924,11,54980,54980,11,55036,55036,11,55092,55092,11,55148,55148,11,55216,55238,9,65056,65071,5,65529,65531,4,68097,68099,5,68159,68159,5,69446,69456,5,69688,69702,5,69808,69810,7,69815,69816,7,69821,69821,1,69888,69890,5,69932,69932,7,69957,69958,7,70016,70017,5,70067,70069,7,70079,70080,7,70089,70092,5,70095,70095,5,70191,70193,5,70196,70196,5,70198,70199,5,70367,70367,5,70371,70378,5,70402,70403,7,70462,70462,5,70464,70464,5,70471,70472,7,70487,70487,5,70502,70508,5,70709,70711,7,70720,70721,7,70725,70725,7,70750,70750,5,70833,70834,7,70841,70841,7,70843,70844,7,70846,70846,7,70849,70849,7,71087,71087,5,71090,71093,5,71100,71101,5,71103,71104,5,71216,71218,7,71227,71228,7,71230,71230,7,71339,71339,5,71341,71341,5,71344,71349,5,71351,71351,5,71456,71457,7,71462,71462,7,71724,71726,7,71736,71736,7,71984,71984,5,71991,71992,7,71997,71997,7,71999,71999,1,72001,72001,1,72003,72003,5,72148,72151,5,72156,72159,7,72164,72164,7,72243,72248,5,72250,72250,1,72263,72263,5,72279,72280,7,72324,72329,1,72343,72343,7,72751,72751,7,72760,72765,5,72767,72767,5,72873,72873,7,72881,72881,7,72884,72884,7,73009,73014,5,73020,73021,5,73030,73030,1,73098,73102,7,73107,73108,7,73110,73110,7,73459,73460,5,78896,78904,4,92976,92982,5,94033,94087,7,94180,94180,5,113821,113822,5,119141,119141,5,119143,119145,5,119150,119154,5,119163,119170,5,119210,119213,5,121344,121398,5,121461,121461,5,121499,121503,5,122880,122886,5,122907,122913,5,122918,122922,5,123628,123631,5,125252,125258,5,126980,126980,14,127183,127183,14,127245,127247,14,127340,127343,14,127358,127359,14,127377,127386,14,127462,127487,6,127491,127503,14,127535,127535,14,127548,127551,14,127568,127569,14,127744,127777,14,127780,127891,14,127894,127895,14,127897,127899,14,127902,127984,14,127987,127989,14,127991,127994,14,128000,128253,14,128255,128317,14,128329,128334,14,128336,128359,14,128367,128368,14,128371,128377,14,128379,128390,14,128392,128393,14,128398,128399,14,128401,128404,14,128407,128419,14,128421,128421,14,128424,128424,14,128433,128434,14,128444,128444,14,128450,128452,14,128465,128467,14,128476,128478,14,128481,128481,14,128483,128483,14,128488,128488,14,128495,128495,14,128499,128499,14,128506,128591,14,128710,128714,14,128721,128722,14,128725,128725,14,128728,128735,14,128742,128744,14,128746,128746,14,128749,128751,14,128753,128754,14,128756,128758,14,128761,128761,14,128763,128764,14,128884,128895,14,128992,129003,14,129036,129039,14,129114,129119,14,129198,129279,14,129293,129295,14,129305,129310,14,129312,129319,14,129328,129328,14,129331,129338,14,129343,129343,14,129351,129355,14,129357,129359,14,129375,129387,14,129393,129393,14,129395,129398,14,129401,129401,14,129403,129403,14,129408,129412,14,129426,129431,14,129443,129444,14,129451,129453,14,129456,129465,14,129472,129472,14,129475,129482,14,129484,129484,14,129488,129510,14,129536,129647,14,129652,129652,14,129656,129658,14,129664,129666,14,129671,129679,14,129686,129704,14,129712,129718,14,129728,129730,14,129744,129750,14,917504,917504,4,917506,917535,4,917632,917759,4,918000,921599,4,0,9,4,11,12,4,14,31,4,169,169,14,174,174,14,1155,1159,5,1425,1469,5,1473,1474,5,1479,1479,5,1552,1562,5,1611,1631,5,1750,1756,5,1759,1764,5,1770,1773,5,1809,1809,5,1958,1968,5,2045,2045,5,2075,2083,5,2089,2093,5,2259,2273,5,2275,2306,5,2362,2362,5,2364,2364,5,2369,2376,5,2381,2381,5,2385,2391,5,2433,2433,5,2492,2492,5,2495,2496,7,2503,2504,7,2509,2509,5,2530,2531,5,2561,2562,5,2620,2620,5,2625,2626,5,2635,2637,5,2672,2673,5,2689,2690,5,2748,2748,5,2753,2757,5,2761,2761,7,2765,2765,5,2810,2815,5,2818,2819,7,2878,2878,5,2880,2880,7,2887,2888,7,2893,2893,5,2903,2903,5,2946,2946,5,3007,3007,7,3009,3010,7,3018,3020,7,3031,3031,5,3073,3075,7,3134,3136,5,3142,3144,5,3157,3158,5,3201,3201,5,3260,3260,5,3263,3263,5,3266,3266,5,3270,3270,5,3274,3275,7,3285,3286,5,3328,3329,5,3387,3388,5,3391,3392,7,3398,3400,7,3405,3405,5,3415,3415,5,3457,3457,5,3530,3530,5,3536,3537,7,3542,3542,5,3551,3551,5,3633,3633,5,3636,3642,5,3761,3761,5,3764,3772,5,3864,3865,5,3895,3895,5,3902,3903,7,3967,3967,7,3974,3975,5,3993,4028,5,4141,4144,5,4146,4151,5,4155,4156,7,4182,4183,7,4190,4192,5,4226,4226,5,4229,4230,5,4253,4253,5,4448,4519,9,4957,4959,5,5938,5940,5,6002,6003,5,6070,6070,7,6078,6085,7,6087,6088,7,6109,6109,5,6158,6158,4,6313,6313,5,6435,6438,7,6441,6443,7,6450,6450,5,6457,6459,5,6681,6682,7,6741,6741,7,6743,6743,7,6752,6752,5,6757,6764,5,6771,6780,5,6832,6845,5,6847,6848,5,6916,6916,7,6965,6965,5,6971,6971,7,6973,6977,7,6979,6980,7,7040,7041,5,7073,7073,7,7078,7079,7,7082,7082,7,7142,7142,5,7144,7145,5,7149,7149,5,7151,7153,5,7204,7211,7,7220,7221,7,7376,7378,5,7393,7393,7,7405,7405,5,7415,7415,7,7616,7673,5,8203,8203,4,8205,8205,13,8232,8232,4,8234,8238,4,8265,8265,14,8293,8293,4,8400,8412,5,8417,8417,5,8421,8432,5,8505,8505,14,8617,8618,14,9000,9000,14,9167,9167,14,9208,9210,14,9642,9643,14,9664,9664,14,9728,9732,14,9735,9741,14,9743,9744,14,9746,9746,14,9750,9751,14,9753,9756,14,9758,9759,14,9761,9761,14,9764,9765,14,9767,9769,14,9771,9773,14,9776,9783,14,9787,9791,14,9793,9793,14,9795,9799,14,9812,9822,14,9824,9824,14,9827,9827,14,9829,9830,14,9832,9832,14,9851,9851,14,9854,9854,14,9856,9861,14,9874,9876,14,9878,9879,14,9881,9881,14,9883,9884,14,9888,9889,14,9895,9895,14,9898,9899,14,9904,9905,14,9917,9918,14,9924,9925,14,9928,9928,14,9934,9935,14,9937,9937,14,9939,9940,14,9961,9962,14,9968,9973,14,9975,9978,14,9981,9981,14,9986,9986,14,9989,9989,14,9998,9998,14,10000,10001,14,10004,10004,14,10013,10013,14,10024,10024,14,10052,10052,14,10060,10060,14,10067,10069,14,10083,10084,14,10133,10135,14,10160,10160,14,10548,10549,14,11035,11036,14,11093,11093,14,11647,11647,5,12330,12333,5,12336,12336,14,12441,12442,5,12953,12953,14,42608,42610,5,42654,42655,5,43010,43010,5,43019,43019,5,43045,43046,5,43052,43052,5,43188,43203,7,43232,43249,5,43302,43309,5,43346,43347,7,43392,43394,5,43443,43443,5,43446,43449,5,43452,43453,5,43493,43493,5,43567,43568,7,43571,43572,7,43587,43587,5,43597,43597,7,43696,43696,5,43703,43704,5,43713,43713,5,43756,43757,5,43765,43765,7,44003,44004,7,44006,44007,7,44009,44010,7,44013,44013,5,44033,44059,12,44061,44087,12,44089,44115,12,44117,44143,12,44145,44171,12,44173,44199,12,44201,44227,12,44229,44255,12,44257,44283,12,44285,44311,12,44313,44339,12,44341,44367,12,44369,44395,12,44397,44423,12,44425,44451,12,44453,44479,12,44481,44507,12,44509,44535,12,44537,44563,12,44565,44591,12,44593,44619,12,44621,44647,12,44649,44675,12,44677,44703,12,44705,44731,12,44733,44759,12,44761,44787,12,44789,44815,12,44817,44843,12,44845,44871,12,44873,44899,12,44901,44927,12,44929,44955,12,44957,44983,12,44985,45011,12,45013,45039,12,45041,45067,12,45069,45095,12,45097,45123,12,45125,45151,12,45153,45179,12,45181,45207,12,45209,45235,12,45237,45263,12,45265,45291,12,45293,45319,12,45321,45347,12,45349,45375,12,45377,45403,12,45405,45431,12,45433,45459,12,45461,45487,12,45489,45515,12,45517,45543,12,45545,45571,12,45573,45599,12,45601,45627,12,45629,45655,12,45657,45683,12,45685,45711,12,45713,45739,12,45741,45767,12,45769,45795,12,45797,45823,12,45825,45851,12,45853,45879,12,45881,45907,12,45909,45935,12,45937,45963,12,45965,45991,12,45993,46019,12,46021,46047,12,46049,46075,12,46077,46103,12,46105,46131,12,46133,46159,12,46161,46187,12,46189,46215,12,46217,46243,12,46245,46271,12,46273,46299,12,46301,46327,12,46329,46355,12,46357,46383,12,46385,46411,12,46413,46439,12,46441,46467,12,46469,46495,12,46497,46523,12,46525,46551,12,46553,46579,12,46581,46607,12,46609,46635,12,46637,46663,12,46665,46691,12,46693,46719,12,46721,46747,12,46749,46775,12,46777,46803,12,46805,46831,12,46833,46859,12,46861,46887,12,46889,46915,12,46917,46943,12,46945,46971,12,46973,46999,12,47001,47027,12,47029,47055,12,47057,47083,12,47085,47111,12,47113,47139,12,47141,47167,12,47169,47195,12,47197,47223,12,47225,47251,12,47253,47279,12,47281,47307,12,47309,47335,12,47337,47363,12,47365,47391,12,47393,47419,12,47421,47447,12,47449,47475,12,47477,47503,12,47505,47531,12,47533,47559,12,47561,47587,12,47589,47615,12,47617,47643,12,47645,47671,12,47673,47699,12,47701,47727,12,47729,47755,12,47757,47783,12,47785,47811,12,47813,47839,12,47841,47867,12,47869,47895,12,47897,47923,12,47925,47951,12,47953,47979,12,47981,48007,12,48009,48035,12,48037,48063,12,48065,48091,12,48093,48119,12,48121,48147,12,48149,48175,12,48177,48203,12,48205,48231,12,48233,48259,12,48261,48287,12,48289,48315,12,48317,48343,12,48345,48371,12,48373,48399,12,48401,48427,12,48429,48455,12,48457,48483,12,48485,48511,12,48513,48539,12,48541,48567,12,48569,48595,12,48597,48623,12,48625,48651,12,48653,48679,12,48681,48707,12,48709,48735,12,48737,48763,12,48765,48791,12,48793,48819,12,48821,48847,12,48849,48875,12,48877,48903,12,48905,48931,12,48933,48959,12,48961,48987,12,48989,49015,12,49017,49043,12,49045,49071,12,49073,49099,12,49101,49127,12,49129,49155,12,49157,49183,12,49185,49211,12,49213,49239,12,49241,49267,12,49269,49295,12,49297,49323,12,49325,49351,12,49353,49379,12,49381,49407,12,49409,49435,12,49437,49463,12,49465,49491,12,49493,49519,12,49521,49547,12,49549,49575,12,49577,49603,12,49605,49631,12,49633,49659,12,49661,49687,12,49689,49715,12,49717,49743,12,49745,49771,12,49773,49799,12,49801,49827,12,49829,49855,12,49857,49883,12,49885,49911,12,49913,49939,12,49941,49967,12,49969,49995,12,49997,50023,12,50025,50051,12,50053,50079,12,50081,50107,12,50109,50135,12,50137,50163,12,50165,50191,12,50193,50219,12,50221,50247,12,50249,50275,12,50277,50303,12,50305,50331,12,50333,50359,12,50361,50387,12,50389,50415,12,50417,50443,12,50445,50471,12,50473,50499,12,50501,50527,12,50529,50555,12,50557,50583,12,50585,50611,12,50613,50639,12,50641,50667,12,50669,50695,12,50697,50723,12,50725,50751,12,50753,50779,12,50781,50807,12,50809,50835,12,50837,50863,12,50865,50891,12,50893,50919,12,50921,50947,12,50949,50975,12,50977,51003,12,51005,51031,12,51033,51059,12,51061,51087,12,51089,51115,12,51117,51143,12,51145,51171,12,51173,51199,12,51201,51227,12,51229,51255,12,51257,51283,12,51285,51311,12,51313,51339,12,51341,51367,12,51369,51395,12,51397,51423,12,51425,51451,12,51453,51479,12,51481,51507,12,51509,51535,12,51537,51563,12,51565,51591,12,51593,51619,12,51621,51647,12,51649,51675,12,51677,51703,12,51705,51731,12,51733,51759,12,51761,51787,12,51789,51815,12,51817,51843,12,51845,51871,12,51873,51899,12,51901,51927,12,51929,51955,12,51957,51983,12,51985,52011,12,52013,52039,12,52041,52067,12,52069,52095,12,52097,52123,12,52125,52151,12,52153,52179,12,52181,52207,12,52209,52235,12,52237,52263,12,52265,52291,12,52293,52319,12,52321,52347,12,52349,52375,12,52377,52403,12,52405,52431,12,52433,52459,12,52461,52487,12,52489,52515,12,52517,52543,12,52545,52571,12,52573,52599,12,52601,52627,12,52629,52655,12,52657,52683,12,52685,52711,12,52713,52739,12,52741,52767,12,52769,52795,12,52797,52823,12,52825,52851,12,52853,52879,12,52881,52907,12,52909,52935,12,52937,52963,12,52965,52991,12,52993,53019,12,53021,53047,12,53049,53075,12,53077,53103,12,53105,53131,12,53133,53159,12,53161,53187,12,53189,53215,12,53217,53243,12,53245,53271,12,53273,53299,12,53301,53327,12,53329,53355,12,53357,53383,12,53385,53411,12,53413,53439,12,53441,53467,12,53469,53495,12,53497,53523,12,53525,53551,12,53553,53579,12,53581,53607,12,53609,53635,12,53637,53663,12,53665,53691,12,53693,53719,12,53721,53747,12,53749,53775,12,53777,53803,12,53805,53831,12,53833,53859,12,53861,53887,12,53889,53915,12,53917,53943,12,53945,53971,12,53973,53999,12,54001,54027,12,54029,54055,12,54057,54083,12,54085,54111,12,54113,54139,12,54141,54167,12,54169,54195,12,54197,54223,12,54225,54251,12,54253,54279,12,54281,54307,12,54309,54335,12,54337,54363,12,54365,54391,12,54393,54419,12,54421,54447,12,54449,54475,12,54477,54503,12,54505,54531,12,54533,54559,12,54561,54587,12,54589,54615,12,54617,54643,12,54645,54671,12,54673,54699,12,54701,54727,12,54729,54755,12,54757,54783,12,54785,54811,12,54813,54839,12,54841,54867,12,54869,54895,12,54897,54923,12,54925,54951,12,54953,54979,12,54981,55007,12,55009,55035,12,55037,55063,12,55065,55091,12,55093,55119,12,55121,55147,12,55149,55175,12,55177,55203,12,55243,55291,10,65024,65039,5,65279,65279,4,65520,65528,4,66045,66045,5,66422,66426,5,68101,68102,5,68152,68154,5,68325,68326,5,69291,69292,5,69632,69632,7,69634,69634,7,69759,69761,5]'); +} + +//#endregion diff --git a/src/vs/base/common/styler.ts b/src/vs/base/common/styler.ts new file mode 100644 index 0000000000000..6ed9d019f75f1 --- /dev/null +++ b/src/vs/base/common/styler.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Color } from 'vs/base/common/color'; + +export type styleFn = (colors: { [name: string]: Color | undefined }) => void; + +export interface IThemable { + style: styleFn; +} diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 460e1a910ae14..eef27600bd73a 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/uint.ts b/src/vs/base/common/uint.ts index b44e0fdaec7bd..347af57eec20d 100644 --- a/src/vs/base/common/uint.ts +++ b/src/vs/base/common/uint.ts @@ -57,12 +57,3 @@ export function toUint32(v: number): number { } return v | 0; } - -export function toUint32Array(arr: number[]): Uint32Array { - const len = arr.length; - const r = new Uint32Array(len); - for (let i = 0; i < len; i++) { - r[i] = toUint32(arr[i]); - } - return r; -} diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index f6a8959a04c9c..5ac44dc842600 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,14 +266,14 @@ 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, - decodeURIComponent(match[4] || _empty), - decodeURIComponent(match[5] || _empty), - decodeURIComponent(match[7] || _empty), - decodeURIComponent(match[9] || _empty), + percentDecode(match[4] || _empty), + percentDecode(match[5] || _empty), + percentDecode(match[7] || _empty), + percentDecode(match[9] || _empty), _strict ); } @@ -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; -// tslint:disable-next-line:class-name -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; @@ -648,3 +675,26 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string { } return res; } + +// --- decode + +function decodeURIComponentGraceful(str: string): string { + try { + return decodeURIComponent(str); + } catch { + if (str.length > 3) { + return str.substr(0, 3) + decodeURIComponentGraceful(str.substr(3)); + } else { + return str; + } + } +} + +const _rEncodedAsHex = /(%[0-9A-Za-z][0-9A-Za-z])+/g; + +function percentDecode(str: string): string { + if (!str.match(_rEncodedAsHex)) { + return str; + } + return str.replace(_rEncodedAsHex, (match) => decodeURIComponentGraceful(match)); +} diff --git a/src/vs/base/common/uuid.ts b/src/vs/base/common/uuid.ts index 3aceb053cdf76..57d9db69de188 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,52 @@ 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@joh node nodejs use `crypto#randomBytes`, see: https://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_randombytes_size_callback +// todo@joh 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 6c9815d3aaa67..479c2ceb32cfc 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -12,7 +12,7 @@ const INITIALIZE = '$initialize'; export interface IWorker extends IDisposable { getId(): number; - postMessage(message: any, transfer: Transferable[]): void; + postMessage(message: any, transfer: ArrayBuffer[]): void; } export interface IWorkerCallback { @@ -302,7 +302,7 @@ export class SimpleWorkerServer { private _requestHandler: IRequestHandler | null; private _protocol: SimpleWorkerProtocol; - constructor(postMessage: (msg: any, transfer?: Transferable[]) => void, requestHandlerFactory: IRequestHandlerFactory | null) { + constructor(postMessage: (msg: any, transfer?: ArrayBuffer[]) => void, requestHandlerFactory: IRequestHandlerFactory | null) { this._requestHandlerFactory = requestHandlerFactory; this._requestHandler = null; this._protocol = new SimpleWorkerProtocol({ diff --git a/src/vs/base/node/config.ts b/src/vs/base/node/config.ts deleted file mode 100644 index 6fac5849f9615..0000000000000 --- 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 f18503ab882c0..7323a92770a1f 100644 --- a/src/vs/base/node/crypto.ts +++ b/src/vs/base/node/crypto.ts @@ -5,19 +5,17 @@ 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) => { 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); @@ -28,8 +26,8 @@ export function checksum(path: string, sha1hash: string | undefined): Promise done(undefined, data.toString('hex'))); + hash.once('error', done); + hash.once('data', (data: Buffer) => done(undefined, data.toString('hex'))); }); return promise.then(hash => { diff --git a/src/vs/base/node/decoder.ts b/src/vs/base/node/decoder.ts index 0e313a5715a67..767cf6d89c114 100644 --- a/src/vs/base/node/decoder.ts +++ b/src/vs/base/node/decoder.ts @@ -15,7 +15,7 @@ import { CharCode } from 'vs/base/common/charCode'; * - forEach() over the result to get the lines */ export class LineDecoder { - private stringDecoder: sd.NodeStringDecoder; + private stringDecoder: sd.StringDecoder; private remaining: string | null; constructor(encoding: string = 'utf8') { diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts deleted file mode 100644 index 0df9dbd26c2da..0000000000000 --- a/src/vs/base/node/encoding.ts +++ /dev/null @@ -1,440 +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 { isLinux, isMacintosh } from 'vs/base/common/platform'; -import { exec } from 'child_process'; -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_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, small number of bytes are enough -const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing - -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_GUESS_BUFFER_MAX_LEN : NO_GUESS_BUFFER_MAX_LEN; - } - - 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): string | 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; - } - - return null; -} - -const MINIMUM_THRESHOLD = 0.2; -const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32']; - -/** - * Guesses the encoding from buffer. - */ -async function guessEncodingByBuffer(buffer: Buffer): Promise { - const jschardet = await import('jschardet'); - - jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD; - - const guessed = jschardet.detect(buffer); - if (!guessed || !guessed.encoding) { - return null; - } - - const enc = guessed.encoding.toLowerCase(); - - // Ignore encodings that cannot guess correctly - // (http://chardet.readthedocs.io/en/latest/supported-encodings.html) - if (0 <= IGNORE_ENCODINGS.indexOf(enc)) { - return null; - } - - 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 }; -} - -// https://ss64.com/nt/chcp.html -const windowsTerminalEncodings = { - '437': 'cp437', // United States - '850': 'cp850', // Multilingual(Latin I) - '852': 'cp852', // Slavic(Latin II) - '855': 'cp855', // Cyrillic(Russian) - '857': 'cp857', // Turkish - '860': 'cp860', // Portuguese - '861': 'cp861', // Icelandic - '863': 'cp863', // Canadian - French - '865': 'cp865', // Nordic - '866': 'cp866', // Russian - '869': 'cp869', // Modern Greek - '936': 'cp936', // Simplified Chinese - '1252': 'cp1252' // West European Latin -}; - -export async function resolveTerminalEncoding(verbose?: boolean): Promise { - let rawEncodingPromise: Promise; - - // Support a global environment variable to win over other mechanics - const cliEncodingEnv = process.env['VSCODE_CLI_ENCODING']; - if (cliEncodingEnv) { - if (verbose) { - console.log(`Found VSCODE_CLI_ENCODING variable: ${cliEncodingEnv}`); - } - - rawEncodingPromise = Promise.resolve(cliEncodingEnv); - } - - // Linux/Mac: use "locale charmap" command - else if (isLinux || isMacintosh) { - rawEncodingPromise = new Promise(resolve => { - if (verbose) { - console.log('Running "locale charmap" to detect terminal encoding...'); - } - - exec('locale charmap', (err, stdout, stderr) => resolve(stdout)); - }); - } - - // Windows: educated guess - else { - rawEncodingPromise = new Promise(resolve => { - if (verbose) { - console.log('Running "chcp" to detect terminal encoding...'); - } - - exec('chcp', (err, stdout, stderr) => { - if (stdout) { - const windowsTerminalEncodingKeys = Object.keys(windowsTerminalEncodings) as Array; - for (const key of windowsTerminalEncodingKeys) { - if (stdout.indexOf(key) >= 0) { - return resolve(windowsTerminalEncodings[key]); - } - } - } - - return resolve(undefined); - }); - }); - } - - const rawEncoding = await rawEncodingPromise; - if (verbose) { - console.log(`Detected raw terminal encoding: ${rawEncoding}`); - } - - if (!rawEncoding || rawEncoding.toLowerCase() === 'utf-8' || rawEncoding.toLowerCase() === UTF8) { - return UTF8; - } - - const iconvEncoding = toIconvLiteEncoding(rawEncoding); - if (iconv.encodingExists(iconvEncoding)) { - return iconvEncoding; - } - - if (verbose) { - console.log('Unsupported terminal encoding, falling back to UTF-8.'); - } - - return UTF8; -} diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index c2faa20632fef..2799ffc718da5 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/pfs.ts b/src/vs/base/node/pfs.ts index 1b294073eed2a..b26e67c4b76b4 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) { - return { stat: linkStat!, isSymbolicLink: false }; + // 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; + } } 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/ports.ts b/src/vs/base/node/ports.ts index d0334628e4777..ba8297dc46ff9 100644 --- a/src/vs/base/node/ports.ts +++ b/src/vs/base/node/ports.ts @@ -72,6 +72,49 @@ function doFindFreePort(startPort: number, giveUpAfter: number, clb: (port: numb client.connect(startPort, '127.0.0.1'); } +/** + * Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener. + */ +export function findFreePortFaster(startPort: number, giveUpAfter: number, timeout: number): Promise { + let resolved: boolean = false; + let timeoutHandle: NodeJS.Timeout | undefined = undefined; + let countTried: number = 1; + const server = net.createServer({ pauseOnConnect: true }); + function doResolve(port: number, resolve: (port: number) => void) { + if (!resolved) { + resolved = true; + server.removeAllListeners(); + server.close(); + if (timeoutHandle) { + clearTimeout(timeoutHandle); + } + resolve(port); + } + } + return new Promise(resolve => { + timeoutHandle = setTimeout(() => { + doResolve(0, resolve); + }, timeout); + + server.on('listening', () => { + doResolve(startPort, resolve); + }); + server.on('error', err => { + if (err && ((err).code === 'EADDRINUSE' || (err).code === 'EACCES') && (countTried < giveUpAfter)) { + startPort++; + countTried++; + server.listen(startPort, '127.0.0.1'); + } else { + doResolve(0, resolve); + } + }); + server.on('close', () => { + doResolve(0, resolve); + }); + server.listen(startPort, '127.0.0.1'); + }); +} + function dispose(socket: net.Socket): void { try { socket.removeAllListeners('connect'); diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index b6f7742e2b3cb..6ccedd679edcb 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -18,7 +18,7 @@ import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, Te import { getPathFromAmdModule } from 'vs/base/common/amd'; 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; @@ -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 = { @@ -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 55c3a73da57d7..31223d4a5d81c 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -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); } }); @@ -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 16b73835bd1db..0000000000000 --- 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 new file mode 100644 index 0000000000000..32ff35ea028a4 --- /dev/null +++ b/src/vs/base/node/terminalEncoding.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * 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 { isWindows } from 'vs/base/common/platform'; + +const windowsTerminalEncodings = { + '437': 'cp437', // United States + '850': 'cp850', // Multilingual(Latin I) + '852': 'cp852', // Slavic(Latin II) + '855': 'cp855', // Cyrillic(Russian) + '857': 'cp857', // Turkish + '860': 'cp860', // Portuguese + '861': 'cp861', // Icelandic + '863': 'cp863', // Canadian - French + '865': 'cp865', // Nordic + '866': 'cp866', // Russian + '869': 'cp869', // Modern Greek + '936': 'cp936', // Simplified Chinese + '1252': 'cp1252' // West European Latin +}; + +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; +} + +const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { + 'ibm866': 'cp866', + 'big5': 'cp950' +}; + +const UTF8 = 'utf8'; + +export async function resolveTerminalEncoding(verbose?: boolean): Promise { + let rawEncodingPromise: Promise; + + // Support a global environment variable to win over other mechanics + const cliEncodingEnv = process.env['VSCODE_CLI_ENCODING']; + if (cliEncodingEnv) { + if (verbose) { + console.log(`Found VSCODE_CLI_ENCODING variable: ${cliEncodingEnv}`); + } + + rawEncodingPromise = Promise.resolve(cliEncodingEnv); + } + + // Windows: educated guess + 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) { + return resolve(windowsTerminalEncodings[key]); + } + } + } + + return resolve(undefined); + }); + }); + } + // Linux/Mac: use "locale charmap" command + else { + rawEncodingPromise = new Promise(resolve => { + if (verbose) { + console.log('Running "locale charmap" to detect terminal encoding...'); + } + + exec('locale charmap', (err, stdout, stderr) => resolve(stdout)); + }); + } + + const rawEncoding = await rawEncodingPromise; + if (verbose) { + console.log(`Detected raw terminal encoding: ${rawEncoding}`); + } + + if (!rawEncoding || rawEncoding.toLowerCase() === 'utf-8' || rawEncoding.toLowerCase() === UTF8) { + return UTF8; + } + + return toIconvLiteEncoding(rawEncoding); +} diff --git a/src/vs/base/node/watcher.ts b/src/vs/base/node/watcher.ts index b51f73c6eaac0..b71a84a63f15a 100644 --- a/src/vs/base/node/watcher.ts +++ b/src/vs/base/node/watcher.ts @@ -10,7 +10,7 @@ import { normalizeNFC } from 'vs/base/common/normalization'; import { toDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { exists, readdir } from 'vs/base/node/pfs'; -export function watchFile(path: string, onChange: (type: 'changed' | 'deleted', path: string) => void, onError: (error: string) => void): IDisposable { +export function watchFile(path: string, onChange: (type: 'added' | 'changed' | 'deleted', path: string) => void, onError: (error: string) => void): IDisposable { return doWatchNonRecursive({ path, isDirectory: false }, onChange, onError); } @@ -189,4 +189,4 @@ function doWatchNonRecursive(file: { path: string, isDirectory: boolean }, onCha watcherDisposables = dispose(watcherDisposables); }); -} \ No newline at end of file +} diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index eaa6fa36ff81f..a0708bf92dfab 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; @@ -80,7 +80,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa 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) => { - _openZip(zipFile, lazy ? { lazyEntries: true } : undefined, (error?: Error, zipfile?: ZipFile) => { + 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 c31a49474f122..0b99dbcb3280a 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 6222be780287a..a7fbfc81e6bd6 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -5,13 +5,14 @@ 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, 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 da008e599309e..4b8917c342631 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 09c97ba47b174..516351e1509c6 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 132654b320fe6..bc8e97c269cce 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 48f7557720638..ad2e7066effdd 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 { @@ -187,7 +223,7 @@ const BufferPresets = { Object: createOneByteBuffer(DataType.Object), }; -declare var Buffer: any; +declare const Buffer: any; const hasBuffer = (typeof Buffer !== 'undefined'); function serialize(writer: IWriter, data: any): void { @@ -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 { @@ -553,7 +633,7 @@ export class ChannelClient implements IChannelClient, IDisposable { } }); - const handler: IHandler = (res: IRawEventFireResponse) => emitter.fire(res.data); + const handler: IHandler = (res: IRawResponse) => emitter.fire((res as IRawEventFireResponse).data); this.handlers.set(id, handler); return emitter.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[${strings.pad(totalLength, 7, ' ')}]%c[len: ${strings.pad(msgLength, 5, ' ')}]%c${strings.pad(req, 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-browser/ipc.electron-browser.ts b/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts deleted file mode 100644 index 2690300b83a69..0000000000000 --- a/src/vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts +++ /dev/null @@ -1,32 +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 { IPCClient } from 'vs/base/parts/ipc/common/ipc'; -import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; -import { ipcRenderer } from 'electron'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { VSBuffer } from 'vs/base/common/buffer'; - -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'); - return new Protocol(ipcRenderer, onMessage); - } - - constructor(id: string) { - const protocol = Client.createProtocol(); - super(protocol, id); - this.protocol = protocol; - } - - dispose(): void { - this.protocol.dispose(); - } -} \ No newline at end of file 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 bac2223ede6ad..b84a1d653dc35 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-sandbox/ipc.electron-sandbox.ts b/src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.ts new file mode 100644 index 0000000000000..19ca487c890d5 --- /dev/null +++ b/src/vs/base/parts/ipc/electron-sandbox/ipc.electron-sandbox.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 { Event } from 'vs/base/common/event'; +import { IPCClient } from 'vs/base/parts/ipc/common/ipc'; +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, 'vscode:message', (_, message) => VSBuffer.wrap(message)); + ipcRenderer.send('vscode:hello'); + return new Protocol(ipcRenderer, onMessage); + } + + constructor(id: string) { + const protocol = Client.createProtocol(); + super(protocol, id); + this.protocol = protocol; + } + + dispose(): void { + this.protocol.dispose(); + } +} diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index d930cdd4003a8..23263105d58e4 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. @@ -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(); @@ -227,7 +234,7 @@ export class Client implements IChannelClient, IDisposable { this.child.on('error', err => console.warn('IPC "' + this.options.serverName + '" errored with ' + err)); this.child.on('exit', (code: any, signal: any) => { - process.removeListener('exit', onExit); + process.removeListener('exit' as 'loaded', onExit); // https://github.com/electron/electron/issues/21475 this.activeRequests.forEach(r => dispose(r)); this.activeRequests.clear(); diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 11e4f516f2244..2b6c70afa7020 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -12,6 +12,7 @@ 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'; export class NodeSocket implements ISocket { public readonly socket: Socket; @@ -56,13 +57,48 @@ export class NodeSocket implements ISocket { // anyways and nodejs is already doing that for us: // > 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 not been allowed to drain. - this.socket.write(buffer.buffer); + // > accept and buffer chunk even if it has not been allowed to drain. + 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 +265,10 @@ export class WebSocketNodeSocket extends Disposable implements ISocket { } } } + + public drain(): Promise { + return this.socket.drain(); + } } function unmask(buffer: VSBuffer, mask: number): void { 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 631e51391332d..0000000000000 --- 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 589acae53d4e4..8ed0f3797f1d9 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/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css new file mode 100644 index 0000000000000..547d47ba0b234 --- /dev/null +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +.quick-input-widget { + position: absolute; + width: 600px; + z-index: 2000; + padding-bottom: 6px; + left: 50%; + margin-left: -300px; +} + +.quick-input-titlebar { + display: flex; +} + +.quick-input-left-action-bar { + display: flex; + margin-left: 4px; + flex: 1; +} + +.quick-input-left-action-bar.monaco-action-bar .actions-container { + justify-content: flex-start; +} + +.quick-input-title { + padding: 3px 0px; + text-align: center; +} + +.quick-input-right-action-bar { + display: flex; + margin-right: 4px; + flex: 1; +} + +.quick-input-titlebar .monaco-action-bar .action-label.codicon { + margin: 0; + width: 19px; + height: 100%; + background-position: center; + background-repeat: no-repeat; +} + +.quick-input-description { + margin: 6px; +} + +.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; + flex-grow: 1; + position: relative; +} + +.quick-input-check-all { + align-self: center; + margin: 0; +} + +.quick-input-filter { + flex-grow: 1; + display: flex; + position: relative; +} + +.quick-input-box { + flex-grow: 1; +} + +.quick-input-widget.show-checkboxes .quick-input-box, +.quick-input-widget.show-checkboxes .quick-input-message { + margin-left: 5px; +} + +.quick-input-visible-count { + position: absolute; + left: -10000px; +} + +.quick-input-count { + 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 { + margin-left: 6px; +} + +.quick-input-action .monaco-text-button { + font-size: 11px; + padding: 0 6px; + display: flex; + height: 100%; + align-items: center; +} + +.quick-input-message { + margin-top: -1px; + padding: 5px 5px 2px 5px; +} + +.quick-input-progress.monaco-progress-container { + position: relative; +} + +.quick-input-progress.monaco-progress-container, +.quick-input-progress.monaco-progress-container .progress-bit { + height: 2px; +} + +.quick-input-list { + line-height: 22px; + 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); +} + +.quick-input-list .quick-input-list-entry { + box-sizing: border-box; + overflow: hidden; + display: flex; + height: 100%; + padding: 0 6px; +} + +.quick-input-list .quick-input-list-entry.quick-input-list-separator-border { + border-top-width: 1px; + border-top-style: solid; +} + +.quick-input-list .monaco-list-row:first-child .quick-input-list-entry.quick-input-list-separator-border { + border-top-style: none; +} + +.quick-input-list .quick-input-list-label { + overflow: hidden; + display: flex; + height: 100%; + flex: 1; +} + +.quick-input-list .quick-input-list-checkbox { + align-self: center; + margin: 0; +} + +.quick-input-list .quick-input-list-rows { + overflow: hidden; + text-overflow: ellipsis; + display: flex; + flex-direction: column; + height: 100%; + flex: 1; + margin-left: 5px; +} + +.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-rows { + margin-left: 10px; +} + +.quick-input-widget .quick-input-list .quick-input-list-checkbox { + display: none; +} +.quick-input-widget.show-checkboxes .quick-input-list .quick-input-list-checkbox { + display: inline; +} + +.quick-input-list .quick-input-list-rows > .quick-input-list-row { + display: flex; + align-items: center; +} + +.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; +} + +.quick-input-list .quick-input-list-rows .monaco-highlighted-label span { + 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; + text-overflow: ellipsis; + overflow: hidden; +} + +.quick-input-list .monaco-highlighted-label .highlight { + font-weight: bold; +} + +.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: 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; + height: 100%; + padding: 0 2px; + vertical-align: middle; +} + +.quick-input-list .quick-input-list-entry-action-bar { + margin-top: 1px; +} + +.quick-input-list .quick-input-list-entry-action-bar { + margin-right: 4px; /* separate from scrollbar */ +} + +.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon { + margin-right: 4px; /* separate actions */ +} + +.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/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts new file mode 100644 index 0000000000000..138c2bce35fcf --- /dev/null +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -0,0 +1,1704 @@ +/*--------------------------------------------------------------------------------------------- + * 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/quickInput'; +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 { CancellationToken } from 'vs/base/common/cancellation'; +import { QuickInputList, QuickInputListFocus } from './quickInputList'; +import { QuickInputBox } from './quickInputBox'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { localize } from 'vs/nls'; +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, 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 { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { equals } from 'vs/base/common/arrays'; +import { TimeoutTimer } from 'vs/base/common/async'; +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 = { + 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; + rightActionBar: ActionBar; + checkAll: HTMLInputElement; + filterContainer: HTMLElement; + inputBox: QuickInputBox; + visibleCountContainer: HTMLElement; + visibleCount: CountBadge; + countContainer: HTMLElement; + count: CountBadge; + okContainer: HTMLElement; + ok: Button; + message: HTMLElement; + customButtonContainer: HTMLElement; + customButton: Button; + progressBar: ProgressBar; + list: QuickInputList; + onDidAccept: Event; + onDidCustom: Event; + onDidTriggerButton: Event; + ignoreFocusOut: boolean; + keyMods: Writeable; + isScreenReaderOptimized(): boolean; + show(controller: QuickInput): void; + setVisibilities(visibilities: Visibilities): void; + setComboboxAccessibility(enabled: boolean): void; + setEnabled(enabled: boolean): void; + setContextKey(contextKey?: string): void; + hide(): void; +} + +type Visibilities = { + title?: boolean; + description?: boolean; + checkAll?: boolean; + inputBox?: boolean; + visibleCount?: boolean; + count?: boolean; + message?: boolean; + list?: boolean; + ok?: boolean; + customButton?: boolean; + progressBar?: boolean; +}; + +class QuickInput extends Disposable implements IQuickInput { + + private _title: string | undefined; + private _description: string | undefined; + private _steps: number | undefined; + private _totalSteps: number | undefined; + protected visible = false; + private _enabled = true; + private _contextKey: string | undefined; + private _busy = false; + private _ignoreFocusOut = false; + private _buttons: IQuickInputButton[] = []; + 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()); + + private busyDelay: TimeoutTimer | undefined; + + constructor( + protected ui: QuickInputUI + ) { + super(); + } + + get title() { + return this._title; + } + + set title(title: string | undefined) { + this._title = title; + this.update(); + } + + get description() { + return this._description; + } + + set description(description: string | undefined) { + this._description = description; + this.update(); + } + + get step() { + return this._steps; + } + + set step(step: number | undefined) { + this._steps = step; + this.update(); + } + + get totalSteps() { + return this._totalSteps; + } + + set totalSteps(totalSteps: number | undefined) { + this._totalSteps = totalSteps; + this.update(); + } + + get enabled() { + return this._enabled; + } + + set enabled(enabled: boolean) { + this._enabled = enabled; + this.update(); + } + + get contextKey() { + return this._contextKey; + } + + set contextKey(contextKey: string | undefined) { + this._contextKey = contextKey; + this.update(); + } + + get busy() { + return this._busy; + } + + set busy(busy: boolean) { + this._busy = busy; + this.update(); + } + + get ignoreFocusOut() { + return this._ignoreFocusOut; + } + + set ignoreFocusOut(ignoreFocusOut: boolean) { + this._ignoreFocusOut = ignoreFocusOut; + this.update(); + } + + get buttons() { + return this._buttons; + } + + set buttons(buttons: IQuickInputButton[]) { + this._buttons = buttons; + this.buttonsUpdated = true; + this.update(); + } + + readonly onDidTriggerButton = this.onDidTriggerButtonEmitter.event; + + show(): void { + if (this.visible) { + return; + } + this.visibleDisposables.add( + this.ui.onDidTriggerButton(button => { + if (this.buttons.indexOf(button) !== -1) { + this.onDidTriggerButtonEmitter.fire(button); + } + }), + ); + this.ui.show(this); + this.visible = true; + this.update(); + } + + hide(): void { + if (!this.visible) { + return; + } + this.ui.hide(); + } + + didHide(): void { + this.visible = false; + this.visibleDisposables.clear(); + this.onDidHideEmitter.fire(); + } + + readonly onDidHide = this.onDidHideEmitter.event; + + protected update() { + if (!this.visible) { + return; + } + const title = this.getTitle(); + 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.busy && !this.busyDelay) { + this.busyDelay = new TimeoutTimer(); + this.busyDelay.setIfNotSet(() => { + if (this.visible) { + this.ui.progressBar.infinite(); + } + }, 800); + } + if (!this.busy && this.busyDelay) { + this.ui.progressBar.stop(); + this.busyDelay.cancel(); + this.busyDelay = undefined; + } + if (this.buttonsUpdated) { + this.buttonsUpdated = false; + 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, async () => { + this.onDidTriggerButtonEmitter.fire(button); + }); + action.tooltip = button.tooltip || ''; + return action; + }), { icon: true, label: false }); + 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, async () => { + this.onDidTriggerButtonEmitter.fire(button); + }); + action.tooltip = button.tooltip || ''; + return action; + }), { icon: true, label: false }); + } + this.ui.ignoreFocusOut = this.ignoreFocusOut; + this.ui.setEnabled(this.enabled); + this.ui.setContextKey(this.contextKey); + } + + private getTitle() { + if (this.title && this.step) { + return `${this.title} (${this.getSteps()})`; + } + if (this.title) { + return this.title; + } + if (this.step) { + return this.getSteps(); + } + return ''; + } + + private getDescription() { + return this.description || ''; + } + + private getSteps() { + if (this.step && this.totalSteps) { + return localize('quickInput.steps', "{0}/{1}", this.step, this.totalSteps); + } + if (this.step) { + return String(this.step); + } + return ''; + } + + protected showMessageDecoration(severity: Severity) { + 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 = ''; + } + } + + readonly onDispose = this.onDisposeEmitter.event; + + dispose(): void { + this.hide(); + this.onDisposeEmitter.fire(); + + super.dispose(); + } +} + +class QuickPick extends QuickInput implements IQuickPick { + + 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 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 = []; + private readonly onDidChangeActiveEmitter = this._register(new Emitter()); + private _selectedItems: T[] = []; + private selectedItemsUpdated = false; + private selectedItemsToConfirm: T[] | null = []; + private readonly onDidChangeSelectionEmitter = this._register(new Emitter()); + private readonly onDidTriggerItemButtonEmitter = this._register(new Emitter>()); + private _valueSelection: Readonly<[number, number]> | undefined; + private valueSelectionUpdated = true; + private _validationMessage: string | undefined; + private _ok: boolean | 'default' = 'default'; + private _customButton = false; + private _customButtonLabel: string | undefined; + private _customButtonHover: string | undefined; + private _quickNavigate: IQuickNavigateConfiguration | undefined; + private _hideInput: boolean | undefined; + + get quickNavigate() { + return this._quickNavigate; + } + + set quickNavigate(quickNavigate: IQuickNavigateConfiguration | undefined) { + this._quickNavigate = quickNavigate; + this.update(); + } + + get value() { + return this._value; + } + + set value(value: string) { + this._value = value || ''; + 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; + } + + set placeholder(placeholder: string | undefined) { + this._placeholder = placeholder; + this.update(); + } + + onDidChangeValue = this.onDidChangeValueEmitter.event; + + onDidAccept = this.onDidAcceptEmitter.event; + + onDidCustom = this.onDidCustomEmitter.event; + + get items() { + return this._items; + } + + set items(items: Array) { + this._items = items; + this.itemsUpdated = true; + this.update(); + } + + get canSelectMany() { + return this._canSelectMany; + } + + set canSelectMany(canSelectMany: boolean) { + this._canSelectMany = canSelectMany; + this.update(); + } + + get canAcceptInBackground() { + return this._canAcceptInBackground; + } + + set canAcceptInBackground(canAcceptInBackground: boolean) { + this._canAcceptInBackground = canAcceptInBackground; + } + + get matchOnDescription() { + return this._matchOnDescription; + } + + set matchOnDescription(matchOnDescription: boolean) { + this._matchOnDescription = matchOnDescription; + this.update(); + } + + get matchOnDetail() { + return this._matchOnDetail; + } + + set matchOnDetail(matchOnDetail: boolean) { + this._matchOnDetail = matchOnDetail; + this.update(); + } + + get matchOnLabel() { + return this._matchOnLabel; + } + + set matchOnLabel(matchOnLabel: boolean) { + this._matchOnLabel = matchOnLabel; + this.update(); + } + + get sortByLabel() { + return this._sortByLabel; + } + + set sortByLabel(sortByLabel: boolean) { + this._sortByLabel = sortByLabel; + this.update(); + } + + get autoFocusOnList() { + return this._autoFocusOnList; + } + + set autoFocusOnList(autoFocusOnList: boolean) { + this._autoFocusOnList = autoFocusOnList; + this.update(); + } + + get itemActivation() { + return this._itemActivation; + } + + set itemActivation(itemActivation: ItemActivation) { + this._itemActivation = itemActivation; + } + + get activeItems() { + return this._activeItems; + } + + set activeItems(activeItems: T[]) { + this._activeItems = activeItems; + this.activeItemsUpdated = true; + this.update(); + } + + onDidChangeActive = this.onDidChangeActiveEmitter.event; + + get selectedItems() { + return this._selectedItems; + } + + set selectedItems(selectedItems: T[]) { + this._selectedItems = selectedItems; + this.selectedItemsUpdated = true; + this.update(); + } + + 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; + } + + set valueSelection(valueSelection: Readonly<[number, number]>) { + this._valueSelection = valueSelection; + this.valueSelectionUpdated = true; + this.update(); + } + + get validationMessage() { + return this._validationMessage; + } + + set validationMessage(validationMessage: string | undefined) { + this._validationMessage = validationMessage; + this.update(); + } + + get customButton() { + return this._customButton; + } + + set customButton(showCustomButton: boolean) { + this._customButton = showCustomButton; + this.update(); + } + + get customLabel() { + return this._customButtonLabel; + } + + set customLabel(label: string | undefined) { + this._customButtonLabel = label; + this.update(); + } + + get customHover() { + return this._customButtonHover; + } + + set customHover(hover: string | undefined) { + this._customButtonHover = hover; + this.update(); + } + + get ok() { + return this._ok; + } + + set ok(showOkButton: boolean | 'default') { + this._ok = showOkButton; + this.update(); + } + + 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(); + } + + onDidChangeSelection = this.onDidChangeSelectionEmitter.event; + + onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event; + + private trySelectFirst() { + if (this.autoFocusOnList) { + if (!this.canSelectMany) { + this.ui.list.focus(QuickInputListFocus.First); + } + } + } + + show() { + if (!this.visible) { + this.visibleDisposables.add( + this.ui.inputBox.onDidChange(value => { + if (value === this.value) { + return; + } + this._value = value; + 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 => { + if (!this.autoFocusOnList) { + this.ui.list.clearFocus(); + } + })); + 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(QuickInputListFocus.Next); + if (this.canSelectMany) { + this.ui.list.domFocus(); + } + dom.EventHelper.stop(event, true); + break; + case KeyCode.UpArrow: + if (this.ui.list.getFocusedElements().length) { + this.ui.list.focus(QuickInputListFocus.Previous); + } else { + this.ui.list.focus(QuickInputListFocus.Last); + } + if (this.canSelectMany) { + this.ui.list.domFocus(); + } + dom.EventHelper.stop(event, true); + break; + case KeyCode.PageDown: + this.ui.list.focus(QuickInputListFocus.NextPage); + if (this.canSelectMany) { + this.ui.list.domFocus(); + } + dom.EventHelper.stop(event, true); + break; + case KeyCode.PageUp: + this.ui.list.focus(QuickInputListFocus.PreviousPage); + if (this.canSelectMany) { + this.ui.list.domFocus(); + } + 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; + } + })); + this.visibleDisposables.add(this.ui.onDidAccept(() => { + if (!this.canSelectMany && this.activeItems[0]) { + this._selectedItems = [this.activeItems[0]]; + this.onDidChangeSelectionEmitter.fire(this.selectedItems); + } + this.onDidAcceptEmitter.fire({ inBackground: false }); + })); + this.visibleDisposables.add(this.ui.onDidCustom(() => { + this.onDidCustomEmitter.fire(); + })); + this.visibleDisposables.add(this.ui.list.onDidChangeFocus(focusedItems => { + if (this.activeItemsUpdated) { + return; // Expect another event. + } + if (this.activeItemsToConfirm !== this._activeItems && equals(focusedItems, this._activeItems, (a, b) => a === b)) { + return; + } + this._activeItems = focusedItems as T[]; + this.onDidChangeActiveEmitter.fire(focusedItems as T[]); + })); + this.visibleDisposables.add(this.ui.list.onDidChangeSelection(({ items: selectedItems, event }) => { + if (this.canSelectMany) { + if (selectedItems.length) { + this.ui.list.setSelectedElements([]); + } + return; + } + if (this.selectedItemsToConfirm !== this._selectedItems && equals(selectedItems, this._selectedItems, (a, b) => a === b)) { + return; + } + this._selectedItems = selectedItems as T[]; + this.onDidChangeSelectionEmitter.fire(selectedItems as T[]); + if (selectedItems.length) { + this.onDidAcceptEmitter.fire({ inBackground: event instanceof MouseEvent && event.button === 1 /* mouse middle click */ }); + } + })); + this.visibleDisposables.add(this.ui.list.onChangedCheckedElements(checkedItems => { + if (!this.canSelectMany) { + return; + } + if (this.selectedItemsToConfirm !== this._selectedItems && equals(checkedItems, this._selectedItems, (a, b) => a === b)) { + return; + } + this._selectedItems = checkedItems as T[]; + this.onDidChangeSelectionEmitter.fire(checkedItems as T[]); + })); + this.visibleDisposables.add(this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event as IQuickPickItemButtonEvent))); + this.visibleDisposables.add(this.registerQuickNavigation()); + this.valueSelectionUpdated = true; + } + super.show(); // TODO: Why have show() bubble up while update() trickles down? (Could move setComboboxAccessibility() here.) + } + + private registerQuickNavigation() { + return dom.addDisposableListener(this.ui.container, dom.EventType.KEY_UP, e => { + if (this.canSelectMany || !this._quickNavigate) { + return; + } + + const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e); + const keyCode = keyboardEvent.keyCode; + + // Select element when keys are pressed that signal it + const quickNavKeys = this._quickNavigate.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 input + } + + 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) { + 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; + } + }); + } + + protected update() { + if (!this.visible) { + return; + } + 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; + } + } + dom.toggleClass(this.ui.container, 'hidden-input', hideInput); + const visibilities: Visibilities = { + title: !!this.title || !!this.step || !!this.buttons.length, + description: !!this.description, + checkAll: 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; + } + if (this.valueSelectionUpdated) { + this.valueSelectionUpdated = false; + this.ui.inputBox.select(this._valueSelection && { start: this._valueSelection[0], end: this._valueSelection[1] }); + } + 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.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()); + 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) { + this.ui.list.clearFocus(); + } else { + this.trySelectFirst(); + } + } + if (this.activeItemsUpdated) { + this.activeItemsUpdated = false; + this.activeItemsToConfirm = this._activeItems; + this.ui.list.setFocusedElements(this.activeItems); + if (this.activeItemsToConfirm === this._activeItems) { + this.activeItemsToConfirm = null; + } + } + if (this.selectedItemsUpdated) { + this.selectedItemsUpdated = false; + this.selectedItemsToConfirm = this._selectedItems; + if (this.canSelectMany) { + this.ui.list.setCheckedElements(this.selectedItems); + } else { + this.ui.list.setSelectedElements(this.selectedItems); + } + if (this.selectedItemsToConfirm === this._selectedItems) { + this.selectedItemsToConfirm = null; + } + } + if (this.validationMessage) { + this.ui.message.textContent = this.validationMessage; + this.showMessageDecoration(Severity.Error); + } else { + this.ui.message.textContent = null; + this.showMessageDecoration(Severity.Ignore); + } + this.ui.customButton.label = this.customLabel || ''; + this.ui.customButton.element.title = this.customHover || ''; + this.ui.setComboboxAccessibility(true); + 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(); + } + } +} + +class InputBox extends QuickInput implements IInputBox { + + private static readonly noPromptMessage = localize('inputModeEntry', "Press 'Enter' to confirm your input or 'Escape' to cancel"); + + private _value = ''; + private _valueSelection: Readonly<[number, number]> | undefined; + private valueSelectionUpdated = true; + private _placeholder: string | undefined; + private _password = false; + private _prompt: string | undefined; + private noValidationMessage = InputBox.noPromptMessage; + private _validationMessage: string | undefined; + private readonly onDidValueChangeEmitter = this._register(new Emitter()); + private readonly onDidAcceptEmitter = this._register(new Emitter()); + + get value() { + return this._value; + } + + set value(value: string) { + this._value = value || ''; + this.update(); + } + + set valueSelection(valueSelection: Readonly<[number, number]>) { + this._valueSelection = valueSelection; + this.valueSelectionUpdated = true; + this.update(); + } + + get placeholder() { + return this._placeholder; + } + + set placeholder(placeholder: string | undefined) { + this._placeholder = placeholder; + this.update(); + } + + get password() { + return this._password; + } + + set password(password: boolean) { + this._password = password; + this.update(); + } + + get prompt() { + return this._prompt; + } + + set prompt(prompt: string | undefined) { + this._prompt = prompt; + this.noValidationMessage = prompt + ? localize('inputModeEntryDescription', "{0} (Press 'Enter' to confirm or 'Escape' to cancel)", prompt) + : InputBox.noPromptMessage; + this.update(); + } + + get validationMessage() { + return this._validationMessage; + } + + set validationMessage(validationMessage: string | undefined) { + this._validationMessage = validationMessage; + this.update(); + } + + readonly onDidChangeValue = this.onDidValueChangeEmitter.event; + + readonly onDidAccept = this.onDidAcceptEmitter.event; + + show() { + if (!this.visible) { + this.visibleDisposables.add( + this.ui.inputBox.onDidChange(value => { + if (value === this.value) { + return; + } + this._value = value; + this.onDidValueChangeEmitter.fire(value); + })); + this.visibleDisposables.add(this.ui.onDidAccept(() => this.onDidAcceptEmitter.fire())); + this.valueSelectionUpdated = true; + } + super.show(); + } + + protected update() { + if (!this.visible) { + return; + } + 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; + } + if (this.valueSelectionUpdated) { + this.valueSelectionUpdated = false; + this.ui.inputBox.select(this._valueSelection && { start: this._valueSelection[0], end: this._valueSelection[1] }); + } + if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { + this.ui.inputBox.placeholder = (this.placeholder || ''); + } + if (this.ui.inputBox.password !== this.password) { + this.ui.inputBox.password = this.password; + } + if (!this.validationMessage && this.ui.message.textContent !== this.noValidationMessage) { + this.ui.message.textContent = this.noValidationMessage; + this.showMessageDecoration(Severity.Ignore); + } + if (this.validationMessage && this.ui.message.textContent !== this.validationMessage) { + this.ui.message.textContent = this.validationMessage; + this.showMessageDecoration(Severity.Error); + } + } +} + +export class QuickInputController extends Disposable { + private static readonly MAX_WIDTH = 600; // Max total width of quick input widget + + private idPrefix: string; + private ui: QuickInputUI | undefined; + private dimension?: dom.IDimension; + private titleBarOffset?: number; + private comboboxAccessibility = false; + private enabled = true; + private readonly onDidAcceptEmitter = this._register(new Emitter()); + private readonly onDidCustomEmitter = this._register(new Emitter()); + private readonly onDidTriggerButtonEmitter = this._register(new Emitter()); + private keyMods: Writeable = { ctrlCmd: false, alt: false }; + + private controller: QuickInput | null = null; + + 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 registerKeyModsListeners() { + 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() { + if (this.ui) { + return this.ui; + } + + 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)); + leftActionBar.domNode.classList.add('quick-input-left-action-bar'); + + const title = dom.append(titleBar, $('.quick-input-title')); + + 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 headerContainer = dom.append(container, $('.quick-input-header')); + + const checkAll = dom.append(headerContainer, $('input.quick-input-check-all')); + checkAll.type = 'checkbox'; + this._register(dom.addStandardDisposableListener(checkAll, dom.EventType.CHANGE, e => { + const checked = checkAll.checked; + list.setAllVisibleChecked(checked); + })); + this._register(dom.addDisposableListener(checkAll, dom.EventType.CLICK, e => { + if (e.x || e.y) { // Avoid 'click' triggered by 'space'... + inputBox.setFocus(); + } + })); + + const extraContainer = dom.append(headerContainer, $('.quick-input-and-message')); + const filterContainer = dom.append(extraContainer, $('.quick-input-filter')); + + const inputBox = this._register(new QuickInputBox(filterContainer)); + inputBox.setAttribute('aria-describedby', `${this.idPrefix}message`); + + const visibleCountContainer = dom.append(filterContainer, $('.quick-input-visible-count')); + visibleCountContainer.setAttribute('aria-live', 'polite'); + visibleCountContainer.setAttribute('aria-atomic', 'true'); + const visibleCount = new CountBadge(visibleCountContainer, { countFormat: 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") }); + + 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") }); + + const okContainer = dom.append(headerContainer, $('.quick-input-action')); + const ok = new Button(okContainer); + ok.label = localize('ok', "OK"); + this._register(ok.onDidClick(e => { + this.onDidAcceptEmitter.fire(); + })); + + const customButtonContainer = dom.append(headerContainer, $('.quick-input-action')); + const customButton = new Button(customButtonContainer); + customButton.label = localize('custom', "Custom"); + this._register(customButton.onDidClick(e => { + this.onDidCustomEmitter.fire(); + })); + + const message = dom.append(extraContainer, $(`#${this.idPrefix}message.quick-input-message`)); + + const progressBar = new ProgressBar(container); + dom.addClass(progressBar.getContainer(), 'quick-input-progress'); + + const list = this._register(new QuickInputList(container, this.idPrefix + 'list', this.options)); + this._register(list.onChangedAllVisibleChecked(checked => { + checkAll.checked = checked; + })); + this._register(list.onChangedVisibleCount(c => { + visibleCount.setCount(c); + })); + this._register(list.onChangedCheckedCount(c => { + count.setCount(c); + })); + this._register(list.onLeave(() => { + // Defer to avoid the input field reacting to the triggering key. + setTimeout(() => { + inputBox.setFocus(); + if (this.controller instanceof QuickPick && this.controller.canSelectMany) { + list.clearFocus(); + } + }, 0); + })); + this._register(list.onDidChangeFocus(() => { + if (this.comboboxAccessibility) { + this.getUI().inputBox.setAttribute('aria-activedescendant', this.getUI().list.getActiveDescendant() || ''); + } + })); + + 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.options.ignoreFocusOut()) { + this.hide(); + } + this.previousFocusElement = undefined; + })); + this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => { + inputBox.setFocus(); + })); + this._register(dom.addDisposableListener(container, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + switch (event.keyCode) { + case KeyCode.Enter: + dom.EventHelper.stop(e, true); + this.onDidAcceptEmitter.fire(); + break; + case KeyCode.Escape: + dom.EventHelper.stop(e, true); + this.hide(); + break; + case KeyCode.Tab: + if (!event.altKey && !event.ctrlKey && !event.metaKey) { + const selectors = ['.action-label.codicon']; + if (container.classList.contains('show-checkboxes')) { + selectors.push('input'); + } else { + selectors.push('input[type=text]'); + } + if (this.getUI().list.isDisplayed()) { + selectors.push('.monaco-list'); + } + const stops = container.querySelectorAll(selectors.join(', ')); + if (event.shiftKey && event.target === stops[0]) { + dom.EventHelper.stop(e, true); + stops[stops.length - 1].focus(); + } else if (!event.shiftKey && event.target === stops[stops.length - 1]) { + dom.EventHelper.stop(e, true); + stops[0].focus(); + } + } + break; + } + })); + + this.ui = { + container, + styleSheet, + leftActionBar, + titleBar, + title, + description, + rightActionBar, + checkAll, + filterContainer, + inputBox, + visibleCountContainer, + visibleCount, + countContainer, + count, + okContainer, + ok, + message, + customButtonContainer, + customButton, + progressBar, + list, + onDidAccept: this.onDidAcceptEmitter.event, + onDidCustom: this.onDidCustomEmitter.event, + onDidTriggerButton: this.onDidTriggerButtonEmitter.event, + ignoreFocusOut: false, + keyMods: this.keyMods, + 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.options.setContextKey(contextKey), + }; + this.updateStyles(); + return this.ui; + } + + 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); + } + doResolve(result); + }; + if (token.isCancellationRequested) { + resolve(undefined); + return; + } + const input = this.createQuickPick(); + let activeItem: T | undefined; + const disposables = [ + input, + input.onDidAccept(() => { + if (input.canSelectMany) { + resolve(input.selectedItems.slice()); + input.hide(); + } else { + const result = input.activeItems[0]; + if (result) { + resolve(result); + input.hide(); + } + } + }), + input.onDidChangeActive(items => { + const focused = items[0]; + if (focused && options.onDidFocus) { + options.onDidFocus(focused); + } + }), + input.onDidChangeSelection(items => { + if (!input.canSelectMany) { + const result = items[0]; + if (result) { + resolve(result); + input.hide(); + } + } + }), + input.onDidTriggerItemButton(event => options.onDidTriggerItemButton && options.onDidTriggerItemButton({ + ...event, + removeItem: () => { + const index = input.items.indexOf(event.item); + if (index !== -1) { + const items = input.items.slice(); + items.splice(index, 1); + input.items = items; + } + } + })), + input.onDidChangeValue(value => { + if (activeItem && !value && (input.activeItems.length !== 1 || input.activeItems[0] !== activeItem)) { + input.activeItems = [activeItem]; + } + }), + token.onCancellationRequested(() => { + input.hide(); + }), + input.onDidHide(() => { + dispose(disposables); + resolve(undefined); + }), + ]; + input.canSelectMany = !!options.canPickMany; + input.placeholder = options.placeHolder; + input.ignoreFocusOut = !!options.ignoreFocusLost; + input.matchOnDescription = !!options.matchOnDescription; + input.matchOnDetail = !!options.matchOnDetail; + input.matchOnLabel = (options.matchOnLabel === undefined) || options.matchOnLabel; // default to true + input.autoFocusOnList = (options.autoFocusOnList === undefined) || options.autoFocusOnList; // default to true + input.quickNavigate = options.quickNavigate; + input.contextKey = options.contextKey; + input.busy = true; + Promise.all[], T | undefined>([picks, options.activeItem]) + .then(([items, _activeItem]) => { + activeItem = _activeItem; + input.busy = false; + input.items = items; + if (input.canSelectMany) { + input.selectedItems = items.filter(item => item.type !== 'separator' && item.picked) as T[]; + } + if (activeItem) { + input.activeItems = [activeItem]; + } + }); + input.show(); + Promise.resolve(picks).then(undefined, err => { + reject(err); + input.hide(); + }); + }); + } + + input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((resolve) => { + if (token.isCancellationRequested) { + resolve(undefined); + return; + } + const input = this.createInputBox(); + const validateInput = options.validateInput || (() => >Promise.resolve(undefined)); + const onDidValueChange = Event.debounce(input.onDidChangeValue, (last, cur) => cur, 100); + let validationValue = options.value || ''; + let validation = Promise.resolve(validateInput(validationValue)); + const disposables = [ + input, + onDidValueChange(value => { + if (value !== validationValue) { + validation = Promise.resolve(validateInput(value)); + validationValue = value; + } + validation.then(result => { + if (value === validationValue) { + input.validationMessage = result || undefined; + } + }); + }), + input.onDidAccept(() => { + const value = input.value; + if (value !== validationValue) { + validation = Promise.resolve(validateInput(value)); + validationValue = value; + } + validation.then(result => { + if (!result) { + resolve(value); + input.hide(); + } else if (value === validationValue) { + input.validationMessage = result; + } + }); + }), + token.onCancellationRequested(() => { + input.hide(); + }), + input.onDidHide(() => { + dispose(disposables); + resolve(undefined); + }), + ]; + input.value = options.value || ''; + input.valueSelection = options.valueSelection; + input.prompt = options.prompt; + input.placeholder = options.placeHolder; + input.password = !!options.password; + input.ignoreFocusOut = !!options.ignoreFocusLost; + input.show(); + }); + } + + backButton = backButton; + + createQuickPick(): IQuickPick { + const ui = this.getUI(); + return new QuickPick(ui); + } + + createInputBox(): IInputBox { + const ui = this.getUI(); + return new InputBox(ui); + } + + private show(controller: QuickInput) { + const ui = this.getUI(); + this.onShowEmitter.fire(); + const oldController = this.controller; + this.controller = controller; + if (oldController) { + oldController.didHide(); + } + + this.setEnabled(true); + ui.leftActionBar.clear(); + ui.title.textContent = ''; + ui.description.textContent = ''; + ui.rightActionBar.clear(); + ui.checkAll.checked = false; + // ui.inputBox.value = ''; Avoid triggering an event. + ui.inputBox.placeholder = ''; + ui.inputBox.password = false; + ui.inputBox.showDecoration(Severity.Ignore); + ui.visibleCount.setCount(0); + ui.count.setCount(0); + ui.message.textContent = ''; + ui.progressBar.stop(); + ui.list.setElements([]); + ui.list.matchOnDescription = false; + ui.list.matchOnDetail = false; + ui.list.matchOnLabel = true; + ui.list.sortByLabel = true; + ui.ignoreFocusOut = false; + this.setComboboxAccessibility(false); + ui.inputBox.ariaLabel = ''; + + const backKeybindingLabel = this.options.backKeybindingLabel(); + backButton.tooltip = backKeybindingLabel ? localize('quickInput.backWithKeybinding', "Back ({0})", backKeybindingLabel) : localize('quickInput.back', "Back"); + + ui.container.style.display = ''; + this.updateLayout(); + ui.inputBox.setFocus(); + } + + private setVisibilities(visibilities: Visibilities) { + const ui = this.getUI(); + ui.title.style.display = visibilities.title ? '' : 'none'; + ui.description.style.display = visibilities.description ? '' : 'none'; + ui.checkAll.style.display = visibilities.checkAll ? '' : 'none'; + ui.filterContainer.style.display = visibilities.inputBox ? '' : 'none'; + ui.visibleCountContainer.style.display = visibilities.visibleCount ? '' : 'none'; + ui.countContainer.style.display = visibilities.count ? '' : 'none'; + 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'); + this.updateLayout(); // TODO + } + + private setComboboxAccessibility(enabled: boolean) { + if (enabled !== this.comboboxAccessibility) { + const ui = this.getUI(); + this.comboboxAccessibility = enabled; + if (this.comboboxAccessibility) { + ui.inputBox.setAttribute('role', 'combobox'); + ui.inputBox.setAttribute('aria-haspopup', 'true'); + ui.inputBox.setAttribute('aria-autocomplete', 'list'); + ui.inputBox.setAttribute('aria-activedescendant', ui.list.getActiveDescendant() || ''); + } else { + ui.inputBox.removeAttribute('role'); + ui.inputBox.removeAttribute('aria-haspopup'); + ui.inputBox.removeAttribute('aria-autocomplete'); + ui.inputBox.removeAttribute('aria-activedescendant'); + } + } + } + + private setEnabled(enabled: boolean) { + if (enabled !== this.enabled) { + this.enabled = enabled; + for (const item of this.getUI().leftActionBar.viewItems) { + (item as ActionViewItem).getAction().enabled = enabled; + } + for (const item of this.getUI().rightActionBar.viewItems) { + (item as ActionViewItem).getAction().enabled = enabled; + } + this.getUI().checkAll.disabled = !enabled; + // this.getUI().inputBox.enabled = enabled; Avoid loosing focus. + this.getUI().ok.enabled = enabled; + this.getUI().list.enabled = enabled; + } + } + + hide() { + const controller = this.controller; + if (controller) { + const focusChanged = !this.ui?.container.contains(document.activeElement); + this.controller = null; + this.onHideEmitter.fire(); + this.getUI().container.style.display = 'none'; + if (!focusChanged) { + if (this.previousFocusElement && this.previousFocusElement.offsetParent) { + this.previousFocusElement.focus(); + this.previousFocusElement = undefined; + } else { + this.options.returnFocus(); + } + } + controller.didHide(); + } + } + + focus() { + if (this.isDisplayed()) { + this.getUI().inputBox.setFocus(); + } + } + + toggle() { + if (this.isDisplayed() && this.controller instanceof QuickPick && this.controller.canSelectMany) { + this.getUI().list.toggleCheckbox(); + } + } + + navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) { + if (this.isDisplayed() && this.getUI().list.isDisplayed()) { + this.getUI().list.focus(next ? QuickInputListFocus.Next : QuickInputListFocus.Previous); + if (quickNavigate && this.controller instanceof QuickPick) { + this.controller.quickNavigate = quickNavigate; + } + } + } + + 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(); + } + + async back() { + this.onDidTriggerButtonEmitter.fire(this.backButton); + } + + async cancel() { + this.hide(); + } + + layout(dimension: dom.IDimension, titleBarOffset: number): void { + this.dimension = dimension; + this.titleBarOffset = titleBarOffset; + this.updateLayout(); + } + + private updateLayout() { + if (this.ui) { + this.ui.container.style.top = `${this.titleBarOffset}px`; + + const style = this.ui.container.style; + const width = Math.min(this.dimension!.width * 0.62 /* golden cut */, QuickInputController.MAX_WIDTH); + style.width = width + 'px'; + style.marginLeft = '-' + (width / 2) + 'px'; + + this.ui.inputBox.layout(); + this.ui.list.layout(this.dimension && this.dimension.height * 0.4); + } + } + + applyStyles(styles: IQuickInputStyles) { + this.styles = styles; + this.updateStyles(); + } + + private updateStyles() { + if (this.ui) { + 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() : ''; + this.ui.container.style.color = quickInputForeground ? quickInputForeground.toString() : ''; + 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; + } + } + } + + private isDisplayed() { + return this.ui && this.ui.container.style.display !== 'none'; + } +} diff --git a/src/vs/base/parts/quickinput/browser/quickInputBox.ts b/src/vs/base/parts/quickinput/browser/quickInputBox.ts new file mode 100644 index 0000000000000..2e796764a6e2c --- /dev/null +++ b/src/vs/base/parts/quickinput/browser/quickInputBox.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * 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/quickInput'; +import * as dom from 'vs/base/browser/dom'; +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'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; + +const $ = dom.$; + +export class QuickInputBox extends Disposable { + + private container: HTMLElement; + private inputBox: InputBox; + + constructor( + private parent: HTMLElement + ) { + super(); + this.container = dom.append(this.parent, $('.quick-input-box')); + this.inputBox = this._register(new InputBox(this.container, undefined)); + } + + onKeyDown = (handler: (event: StandardKeyboardEvent) => void): IDisposable => { + return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + handler(new StandardKeyboardEvent(e)); + }); + }; + + onMouseDown = (handler: (event: StandardMouseEvent) => void): IDisposable => { + return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => { + handler(new StandardMouseEvent(e)); + }); + }; + + onDidChange = (handler: (event: string) => void): IDisposable => { + return this.inputBox.onDidChange(handler); + }; + + get value() { + return this.inputBox.value; + } + + set value(value: string) { + this.inputBox.value = value; + } + + select(range: IRange | null = null): void { + this.inputBox.select(range); + } + + isSelectionAtEnd(): boolean { + return this.inputBox.isSelectionAtEnd(); + } + + setPlaceholder(placeholder: string): void { + this.inputBox.setPlaceHolder(placeholder); + } + + get placeholder() { + return this.inputBox.inputElement.getAttribute('placeholder') || ''; + } + + set placeholder(placeholder: string) { + 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'; + } + + set password(password: boolean) { + this.inputBox.inputElement.type = password ? 'password' : 'text'; + } + + set enabled(enabled: boolean) { + this.inputBox.setEnabled(enabled); + } + + hasFocus(): boolean { + return this.inputBox.hasFocus(); + } + + setAttribute(name: string, value: string): void { + this.inputBox.inputElement.setAttribute(name, value); + } + + removeAttribute(name: string): void { + this.inputBox.inputElement.removeAttribute(name); + } + + showDecoration(decoration: Severity): void { + if (decoration === Severity.Ignore) { + this.inputBox.hideMessage(); + } else { + this.inputBox.showMessage({ type: decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR, content: '' }); + } + } + + stylesForType(decoration: Severity) { + return this.inputBox.stylesForType(decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR); + } + + setFocus(): void { + this.inputBox.focus(); + } + + layout(): void { + this.inputBox.layout(); + } + + style(styles: IInputBoxStyles): void { + this.inputBox.style(styles); + } +} diff --git a/src/vs/base/parts/quickinput/browser/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts new file mode 100644 index 0000000000000..7c98a6bd88027 --- /dev/null +++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts @@ -0,0 +1,731 @@ +/*--------------------------------------------------------------------------------------------- + * 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/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 { 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'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { memoize } from 'vs/base/common/decorators'; +import { range } from 'vs/base/common/arrays'; +import * as platform from 'vs/base/common/platform'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { getIconClass } from 'vs/base/parts/quickinput/browser/quickInputUtils'; +import { withNullAsUndefined } from 'vs/base/common/types'; +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.$; + +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, IDisposable { + index!: number; + item!: IQuickPickItem; + saneLabel!: string; + saneAriaLabel!: string; + saneDescription?: string; + saneDetail?: string; + hidden = false; + private readonly _onChecked = new Emitter(); + onChecked = this._onChecked.event; + _checked?: boolean; + get checked() { + return !!this._checked; + } + set checked(value: boolean) { + if (value !== this._checked) { + this._checked = value; + this._onChecked.fire(value); + } + } + separator?: IQuickPickSeparator; + labelHighlights?: IMatch[]; + descriptionHighlights?: IMatch[]; + detailHighlights?: IMatch[]; + fireButtonTriggered!: (event: IQuickPickItemButtonEvent) => void; + + constructor(init: IListElement) { + assign(this, init); + } + + dispose() { + this._onChecked.dispose(); + } +} + +interface IListElementTemplateData { + entry: HTMLDivElement; + checkbox: HTMLInputElement; + label: IconLabel; + keybinding: KeybindingLabel; + detail: HighlightedLabel; + separator: HTMLDivElement; + actionBar: ActionBar; + element: ListElement; + toDisposeElement: IDisposable[]; + toDisposeTemplate: IDisposable[]; +} + +class ListElementRenderer implements IListRenderer { + + static readonly ID = 'listelement'; + + get templateId() { + return ListElementRenderer.ID; + } + + renderTemplate(container: HTMLElement): IListElementTemplateData { + const data: IListElementTemplateData = Object.create(null); + data.toDisposeElement = []; + data.toDisposeTemplate = []; + + data.entry = dom.append(container, $('.quick-input-list-entry')); + + // Checkbox + const label = dom.append(data.entry, $('label.quick-input-list-label')); + data.toDisposeTemplate.push(dom.addStandardDisposableListener(label, dom.EventType.CLICK, e => { + 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 => { + data.element.checked = data.checkbox.checked; + })); + + // Rows + const rows = dom.append(label, $('.quick-input-list-rows')); + const row1 = dom.append(rows, $('.quick-input-list-row')); + const row2 = dom.append(rows, $('.quick-input-list-row')); + + // Label + data.label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportCodicons: true }); + + // Keybinding + const keybindingContainer = dom.append(row1, $('.quick-input-list-entry-keybinding')); + data.keybinding = new KeybindingLabel(keybindingContainer, platform.OS); + + // Detail + const detailContainer = dom.append(row2, $('.quick-input-list-label-meta')); + data.detail = new HighlightedLabel(detailContainer, true); + + // Separator + data.separator = dom.append(data.entry, $('.quick-input-list-separator')); + + // Actions + data.actionBar = new ActionBar(data.entry); + data.actionBar.domNode.classList.add('quick-input-list-entry-action-bar'); + data.toDisposeTemplate.push(data.actionBar); + + return data; + } + + renderElement(element: ListElement, index: number, data: IListElementTemplateData): void { + data.toDisposeElement = dispose(data.toDisposeElement); + data.element = element; + data.checkbox.checked = element.checked; + data.toDisposeElement.push(element.onChecked(checked => data.checkbox.checked = checked)); + + const { labelHighlights, descriptionHighlights, detailHighlights } = element; + + // Label + const options: IIconLabelValueOptions = Object.create(null); + options.matches = labelHighlights || []; + options.descriptionTitle = element.saneDescription; + options.descriptionMatches = descriptionHighlights || []; + options.extraClasses = element.item.iconClasses; + options.italic = element.item.italic; + options.strikethrough = element.item.strikethrough; + data.label.setLabel(element.saneLabel, element.saneDescription, options); + + // Keybinding + data.keybinding.set(element.item.keybinding); + + // Meta + data.detail.set(element.saneDetail, detailHighlights); + + // Separator + if (element.separator && element.separator.label) { + data.separator.textContent = element.separator.label; + data.separator.style.display = ''; + } else { + data.separator.style.display = 'none'; + } + if (element.separator) { + dom.addClass(data.entry, 'quick-input-list-separator-border'); + } else { + dom.removeClass(data.entry, 'quick-input-list-separator-border'); + } + + // Actions + data.actionBar.clear(); + const buttons = element.item.buttons; + if (buttons && buttons.length) { + data.actionBar.push(buttons.map((button, index) => { + 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 + }); + return Promise.resolve(); + }); + action.tooltip = button.tooltip || ''; + return action; + }), { icon: true, label: false }); + dom.addClass(data.entry, 'has-actions'); + } else { + dom.removeClass(data.entry, 'has-actions'); + } + } + + disposeElement(element: ListElement, index: number, data: IListElementTemplateData): void { + data.toDisposeElement = dispose(data.toDisposeElement); + } + + disposeTemplate(data: IListElementTemplateData): void { + data.toDisposeElement = dispose(data.toDisposeElement); + data.toDisposeTemplate = dispose(data.toDisposeTemplate); + } +} + +class ListElementDelegate implements IListVirtualDelegate { + + getHeight(element: ListElement): number { + return element.saneDetail ? 44 : 22; + } + + getTemplateId(element: ListElement): string { + return ListElementRenderer.ID; + } +} + +export enum QuickInputListFocus { + First = 1, + Second, + Last, + Next, + Previous, + NextPage, + PreviousPage +} + +export class QuickInputList { + + readonly id: string; + private container: HTMLElement; + private list: List; + private inputElements: Array = []; + private elements: ListElement[] = []; + private elementsToIndexes = new Map(); + matchOnDescription = false; + matchOnDetail = false; + matchOnLabel = true; + sortByLabel = true; + private readonly _onChangedAllVisibleChecked = new Emitter(); + onChangedAllVisibleChecked: Event = this._onChangedAllVisibleChecked.event; + private readonly _onChangedCheckedCount = new Emitter(); + onChangedCheckedCount: Event = this._onChangedCheckedCount.event; + private readonly _onChangedVisibleCount = new Emitter(); + onChangedVisibleCount: Event = this._onChangedVisibleCount.event; + private readonly _onChangedCheckedElements = new Emitter(); + 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; + private elementDisposables: IDisposable[] = []; + private disposables: IDisposable[] = []; + + constructor( + private parent: HTMLElement, + id: string, + options: IQuickInputOptions, + ) { + this.id = id; + this.container = dom.append(this.parent, $('.quick-input-list')); + const delegate = new ListElementDelegate(); + const accessibilityProvider = new QuickInputAccessibilityProvider(); + this.list = options.createList('QuickInput', this.container, delegate, [new ListElementRenderer()], { + identityProvider: { getId: element => element.saneLabel }, + setRowLineHeight: false, + multipleSelectionSupport: false, + horizontalScrolling: false, + accessibilityProvider + } as IListOptions); + this.list.getHTMLElement().id = id; + this.disposables.push(this.list); + this.disposables.push(this.list.onKeyDown(e => { + const event = new StandardKeyboardEvent(e); + switch (event.keyCode) { + case KeyCode.Space: + this.toggleCheckbox(); + break; + case KeyCode.KEY_A: + if (platform.isMacintosh ? e.metaKey : e.ctrlKey) { + this.list.setFocus(range(this.list.length)); + } + break; + case KeyCode.UpArrow: + const focus1 = this.list.getFocus(); + if (focus1.length === 1 && focus1[0] === 0) { + this._onLeave.fire(); + } + break; + case KeyCode.DownArrow: + 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) { + // Works around / fixes #64350. + e.browserEvent.preventDefault(); + } + })); + this.disposables.push(dom.addDisposableListener(this.container, dom.EventType.CLICK, e => { + if (e.x || e.y) { // Avoid 'click' triggered by 'space' on checkbox. + 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.onDidChangeFocus, e => e.elements.map(e => e.item)); + } + + @memoize + get onDidChangeSelection() { + return Event.map(this.list.onDidChangeSelection, e => ({ items: e.elements.map(e => e.item), event: e.browserEvent })); + } + + getAllVisibleChecked() { + return this.allVisibleChecked(this.elements, false); + } + + private allVisibleChecked(elements: ListElement[], whenNoneVisible = true) { + for (let i = 0, n = elements.length; i < n; i++) { + const element = elements[i]; + if (!element.hidden) { + if (!element.checked) { + return false; + } else { + whenNoneVisible = true; + } + } + } + return whenNoneVisible; + } + + getCheckedCount() { + let count = 0; + const elements = this.elements; + for (let i = 0, n = elements.length; i < n; i++) { + if (elements[i].checked) { + count++; + } + } + return count; + } + + getVisibleCount() { + let count = 0; + const elements = this.elements; + for (let i = 0, n = elements.length; i < n; i++) { + if (!elements[i].hidden) { + count++; + } + } + return count; + } + + setAllVisibleChecked(checked: boolean) { + try { + this._fireCheckedEvents = false; + this.elements.forEach(element => { + if (!element.hidden) { + element.checked = checked; + } + }); + } finally { + this._fireCheckedEvents = true; + this.fireCheckedEvents(); + } + } + + setElements(inputElements: Array): void { + this.elementDisposables = dispose(this.elementDisposables); + const fireButtonTriggered = (event: IQuickPickItemButtonEvent) => this.fireButtonTriggered(event); + this.inputElements = inputElements; + 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, + 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 + })); + } + 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) => { + map.set(element.item, index); + return map; + }, new Map()); + this.list.splice(0, this.list.length); // Clear focus and selection first, sending the events when the list is empty. + this.list.splice(0, this.list.length, this.elements); + this._onChangedVisibleCount.fire(this.elements.length); + } + + getElementsCount(): number { + return this.inputElements.length; + } + + getFocusedElements() { + return this.list.getFocusedElements() + .map(e => e.item); + } + + setFocusedElements(items: IQuickPickItem[]) { + this.list.setFocus(items + .filter(item => this.elementsToIndexes.has(item)) + .map(item => this.elementsToIndexes.get(item)!)); + if (items.length > 0) { + const focused = this.list.getFocus()[0]; + if (typeof focused === 'number') { + this.list.reveal(focused); + } + } + } + + getActiveDescendant() { + return this.list.getHTMLElement().getAttribute('aria-activedescendant'); + } + + getSelectedElements() { + return this.list.getSelectedElements() + .map(e => e.item); + } + + setSelectedElements(items: IQuickPickItem[]) { + this.list.setSelection(items + .filter(item => this.elementsToIndexes.has(item)) + .map(item => this.elementsToIndexes.get(item)!)); + } + + getCheckedElements() { + return this.elements.filter(e => e.checked) + .map(e => e.item); + } + + setCheckedElements(items: IQuickPickItem[]) { + try { + this._fireCheckedEvents = false; + const checked = new Set(); + for (const item of items) { + checked.add(item); + } + for (const element of this.elements) { + element.checked = checked.has(element.item); + } + } finally { + this._fireCheckedEvents = true; + this.fireCheckedEvents(); + } + } + + set enabled(value: boolean) { + this.list.getHTMLElement().style.pointerEvents = value ? '' : 'none'; + } + + focus(what: QuickInputListFocus): void { + if (!this.list.length) { + return; + } + + if (what === QuickInputListFocus.Next && this.list.getFocus()[0] === this.list.length - 1) { + what = QuickInputListFocus.First; + } + + 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() { + this.list.setFocus([]); + } + + domFocus() { + this.list.domFocus(); + } + + layout(maxHeight?: number): void { + this.list.getHTMLElement().style.maxHeight = maxHeight ? `calc(${Math.floor(maxHeight / 44) * 44}px)` : ''; + this.list.layout(); + } + + filter(query: string): boolean { + if (!(this.sortByLabel || this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) { + this.list.layout(); + return false; + } + query = query.trim(); + + // Reset filtering + if (!query || !(this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) { + this.elements.forEach(element => { + element.labelHighlights = undefined; + element.descriptionHighlights = undefined; + element.detailHighlights = undefined; + element.hidden = false; + const previous = element.index && this.inputElements[element.index - 1]; + element.separator = previous && previous.type === 'separator' ? previous : undefined; + }); + } + + // Filter by value (since we support codicons, use codicon aware fuzzy matching) + else { + this.elements.forEach(element => { + const labelHighlights = this.matchOnLabel ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneLabel))) : undefined; + const descriptionHighlights = this.matchOnDescription ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneDescription || ''))) : undefined; + const detailHighlights = this.matchOnDetail ? withNullAsUndefined(matchesFuzzyCodiconAware(query, parseCodicons(element.saneDetail || ''))) : undefined; + + if (labelHighlights || descriptionHighlights || detailHighlights) { + element.labelHighlights = labelHighlights; + element.descriptionHighlights = descriptionHighlights; + element.detailHighlights = detailHighlights; + element.hidden = false; + } else { + element.labelHighlights = undefined; + element.descriptionHighlights = undefined; + element.detailHighlights = undefined; + element.hidden = !element.item.alwaysShow; + } + element.separator = undefined; + }); + } + + const shownElements = this.elements.filter(element => !element.hidden); + + // Sort by value + if (this.sortByLabel && query) { + const normalizedSearchValue = query.toLowerCase(); + shownElements.sort((a, b) => { + return compareEntries(a, b, normalizedSearchValue); + }); + } + + this.elementsToIndexes = shownElements.reduce((map, element, index) => { + map.set(element.item, index); + return map; + }, new Map()); + this.list.splice(0, this.list.length, shownElements); + this.list.setFocus([]); + this.list.layout(); + + this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked()); + this._onChangedVisibleCount.fire(shownElements.length); + + return true; + } + + toggleCheckbox() { + try { + this._fireCheckedEvents = false; + const elements = this.list.getFocusedElements(); + const allChecked = this.allVisibleChecked(elements); + for (const element of elements) { + element.checked = !allChecked; + } + } finally { + this._fireCheckedEvents = true; + this.fireCheckedEvents(); + } + } + + display(display: boolean) { + this.container.style.display = display ? '' : 'none'; + } + + isDisplayed() { + return this.container.style.display !== 'none'; + } + + dispose() { + this.elementDisposables = dispose(this.elementDisposables); + this.disposables = dispose(this.disposables); + } + + private fireCheckedEvents() { + if (this._fireCheckedEvents) { + this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked()); + this._onChangedCheckedCount.fire(this.getCheckedCount()); + this._onChangedCheckedElements.fire(this.getCheckedElements()); + } + } + + private fireButtonTriggered(event: IQuickPickItemButtonEvent) { + this._onButtonTriggered.fire(event); + } + + style(styles: IListStyles) { + this.list.style(styles); + } +} + +function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number { + + const labelHighlightsA = elementA.labelHighlights || []; + const labelHighlightsB = elementB.labelHighlights || []; + if (labelHighlightsA.length && !labelHighlightsB.length) { + return -1; + } + + if (!labelHighlightsA.length && labelHighlightsB.length) { + return 1; + } + + if (labelHighlightsA.length === 0 && labelHighlightsB.length === 0) { + return 0; + } + + return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor); +} + +class QuickInputAccessibilityProvider implements IListAccessibilityProvider { + + getWidgetAriaLabel(): string { + return localize('quickInput', "Quick Input"); + } + + getAriaLabel(element: ListElement): string | null { + return element.saneAriaLabel; + } + + getWidgetRole() { + return 'listbox'; + } + + getRole() { + return 'option'; + } +} 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 0000000000000..de7339e675710 --- /dev/null +++ b/src/vs/base/parts/quickinput/common/quickInput.ts @@ -0,0 +1,363 @@ +/*--------------------------------------------------------------------------------------------- + * 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; +} + +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 463323c236361..0000000000000 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ /dev/null @@ -1,577 +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'; - -export interface IContext { - event: any; - quickNavigateConfiguration: IQuickNavigateConfiguration; -} - -export interface IHighlight { - 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(types.withNullAsUndefined(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 a87b30dbadc08..0000000000000 --- 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 7a3a473d97005..0000000000000 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ /dev/null @@ -1,1029 +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'; - -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 { - - 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); - } - })); - - 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 = keyCode === KeyCode.Enter || 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() : null; - 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 b6a9b9247d0d4..0000000000000 --- 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-description-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 d6039c688028b..0000000000000 --- a/src/vs/base/parts/quickopen/common/quickOpen.ts +++ /dev/null @@ -1,97 +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; - // rationale: will be replaced by quickinput later - // tslint:disable-next-line: no-dom-globals - renderTemplate(templateId: string, container: 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/common/quickOpenScorer.ts b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts deleted file mode 100644 index 21cb08caaceb5..0000000000000 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ /dev/null @@ -1,635 +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 { compareAnything } from 'vs/base/common/comparers'; -import { matchesPrefix, IMatch, matchesCamelCase, isUpper } 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 }; - -const NO_MATCH = 0; -const NO_SCORE: Score = [NO_MATCH, []]; - -// const DEBUG = false; -// const DEBUG_MATRIX = false; - -export function score(target: string, query: string, queryLower: string, fuzzy: boolean): Score { - if (!target || !query) { - return NO_SCORE; // return early if target or query are undefined - } - - const targetLength = target.length; - const queryLength = query.length; - - if (targetLength < queryLength) { - return NO_SCORE; // impossible for query to be contained in target - } - - // if (DEBUG) { - // console.group(`Target: ${target}, Query: ${query}`); - // } - - const targetLower = target.toLowerCase(); - - // 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 (DEBUG) { - // console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`); - // } - - return NO_SCORE; - } - } - - const res = doScore(query, queryLower, queryLength, target, targetLower, targetLength); - - // if (DEBUG) { - // console.log(`%cFinal Score: ${res[0]}`, 'font-weight: bold'); - // console.groupEnd(); - // } - - return res; -} - -function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): Score { - const scores: number[] = []; - const matches: number[] = []; - - // - // Build Scorer Matrix: - // - // The matrix is composed of query q and target t. For each index we score - // q[i] with t[i] and compare that with the previous score. If the score is - // equal or larger, we keep the match. In addition to the score, we also keep - // the length of the consecutive matches to use as boost for the score. - // - // t a r g e t - // q - // u - // e - // r - // y - // - for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) { - const queryIndexOffset = queryIndex * targetLength; - const queryIndexPreviousOffset = queryIndexOffset - targetLength; - - const queryIndexGtNull = queryIndex > 0; - - const queryCharAtIndex = query[queryIndex]; - const queryLowerCharAtIndex = queryLower[queryIndex]; - - for (let targetIndex = 0; targetIndex < targetLength; targetIndex++) { - const targetIndexGtNull = targetIndex > 0; - - const currentIndex = queryIndexOffset + targetIndex; - const leftIndex = currentIndex - 1; - const diagIndex = queryIndexPreviousOffset + targetIndex - 1; - - const leftScore = targetIndexGtNull ? scores[leftIndex] : 0; - const diagScore = queryIndexGtNull && targetIndexGtNull ? scores[diagIndex] : 0; - - const matchesSequenceLength = queryIndexGtNull && targetIndexGtNull ? matches[diagIndex] : 0; - - // If we are not matching on the first query character any more, we only produce a - // score if we had a score previously for the last query index (by looking at the diagScore). - // This makes sure that the query always matches in sequence on the target. For example - // given a target of "ede" and a query of "de", we would otherwise produce a wrong high score - // for query[1] ("e") matching on target[0] ("e") because of the "beginning of word" boost. - let score: number; - if (!diagScore && queryIndexGtNull) { - score = 0; - } else { - score = computeCharScore(queryCharAtIndex, queryLowerCharAtIndex, target, targetLower, targetIndex, matchesSequenceLength); - } - - // We have a score and its equal or larger than the left score - // Match: sequence continues growing from previous diag value - // Score: increases by diag score value - if (score && diagScore + score >= leftScore) { - matches[currentIndex] = matchesSequenceLength + 1; - scores[currentIndex] = diagScore + score; - } - - // We either have no score or the score is lower than the left score - // Match: reset to 0 - // Score: pick up from left hand side - else { - matches[currentIndex] = NO_MATCH; - scores[currentIndex] = leftScore; - } - } - } - - // Restore Positions (starting from bottom right of matrix) - const positions: number[] = []; - let queryIndex = queryLength - 1; - let targetIndex = targetLength - 1; - while (queryIndex >= 0 && targetIndex >= 0) { - const currentIndex = queryIndex * targetLength + targetIndex; - const match = matches[currentIndex]; - if (match === NO_MATCH) { - targetIndex--; // go left - } else { - positions.push(targetIndex); - - // go up and left - queryIndex--; - targetIndex--; - } - } - - // Print matrix - // if (DEBUG_MATRIX) { - // printMatrix(query, target, matches, scores); - // } - - return [scores[queryLength * targetLength - 1], positions.reverse()]; -} - -function computeCharScore(queryCharAtIndex: string, queryLowerCharAtIndex: string, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { - let score = 0; - - if (queryLowerCharAtIndex !== targetLower[targetIndex]) { - return score; // no match of characters - } - - // Character match bonus - score += 1; - - // if (DEBUG) { - // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLower[queryIndex]} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal'); - // } - - // Consecutive match bonus - if (matchesSequenceLength > 0) { - score += (matchesSequenceLength * 5); - - // if (DEBUG) { - // console.log('Consecutive match bonus: ' + (matchesSequenceLength * 5)); - // } - } - - // Same case bonus - if (queryCharAtIndex === target[targetIndex]) { - score += 1; - - // if (DEBUG) { - // console.log('Same case bonus: +1'); - // } - } - - // Start of word bonus - if (targetIndex === 0) { - score += 8; - - // if (DEBUG) { - // console.log('Start of word bonus: +8'); - // } - } - - else { - - // After separator bonus - const separatorBonus = scoreSeparatorAtPos(target.charCodeAt(targetIndex - 1)); - if (separatorBonus) { - score += separatorBonus; - - // if (DEBUG) { - // console.log('After separtor bonus: +4'); - // } - } - - // Inside word upper case bonus (camel case) - else if (isUpper(target.charCodeAt(targetIndex))) { - score += 1; - - // if (DEBUG) { - // console.log('Inside word upper case bonus: +1'); - // } - } - } - - // if (DEBUG) { - // console.groupEnd(); - // } - - return score; -} - -function scoreSeparatorAtPos(charCode: number): number { - switch (charCode) { - case CharCode.Slash: - case CharCode.Backslash: - return 5; // prefer path separators... - case CharCode.Underline: - case CharCode.Dash: - case CharCode.Period: - case CharCode.Space: - case CharCode.SingleQuote: - case CharCode.DoubleQuote: - case CharCode.Colon: - return 4; // ...over other separators - default: - return 0; - } -} - -// function printMatrix(query: string, target: string, matches: number[], scores: number[]): void { -// console.log('\t' + target.split('').join('\t')); -// for (let queryIndex = 0; queryIndex < query.length; queryIndex++) { -// let line = query[queryIndex] + '\t'; -// for (let targetIndex = 0; targetIndex < target.length; targetIndex++) { -// const currentIndex = queryIndex * target.length + targetIndex; -// line = line + 'M' + matches[currentIndex] + '/' + 'S' + scores[currentIndex] + '\t'; -// } - -// console.log(line); -// } -// } - -/** - * Scoring on structural items that have a label and optional description. - */ -export interface IItemScore { - - /** - * Overall score. - */ - score: number; - - /** - * Matches within the label. - */ - labelMatch?: IMatch[]; - - /** - * Matches within the description. - */ - descriptionMatch?: IMatch[]; -} - -const NO_ITEM_SCORE: IItemScore = Object.freeze({ score: 0 }); - -export interface IItemAccessor { - - /** - * Just the label of the item to score on. - */ - getItemLabel(item: T): string | null; - - /** - * The optional description of the item to score on. Can be null. - */ - getItemDescription(item: T): string | null; - - /** - * If the item is a file, the path of the file to score on. Can be null. - */ - 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; - -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) { - return NO_ITEM_SCORE; // we need an item and query to score on at least - } - - const label = accessor.getItemLabel(item); - if (!label) { - return NO_ITEM_SCORE; // we need a label at least - } - - const description = accessor.getItemDescription(item); - - let cacheHash: string; - if (description) { - cacheHash = `${label}${description}${query.value}${fuzzy}`; - } else { - cacheHash = `${label}${query.value}${fuzzy}`; - } - - const cached = cache[cacheHash]; - if (cached) { - return cached; - } - - const itemScore = doScoreItem(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 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))) { - 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) { - - // 2.) treat prefix matches on the label second highest - const prefixLabelMatch = matchesPrefix(query.value, label); - if (prefixLabelMatch) { - return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch }; - } - - // 3.) treat camelcase matches on the label third highest - const camelcaseLabelMatch = matchesCamelCase(query.value, label); - if (camelcaseLabelMatch) { - return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch }; - } - - // 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) }; - } - } - - // 5.) finally compute description + label scores if we have a description - if (description) { - let descriptionPrefix = description; - if (!!path) { - descriptionPrefix = `${description}${sep}`; // assume this is a file path - } - - const descriptionPrefixLength = descriptionPrefix.length; - const descriptionAndLabel = `${descriptionPrefix}${label}`; - - const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query.value, query.lowercase, fuzzy); - if (labelDescriptionScore) { - const labelDescriptionMatches = createMatches(labelDescriptionPositions); - const labelMatch: IMatch[] = []; - const descriptionMatch: IMatch[] = []; - - // We have to split the matches back onto the label and description portions - labelDescriptionMatches.forEach(h => { - - // Match overlaps label and description part, we need to split it up - if (h.start < descriptionPrefixLength && h.end > descriptionPrefixLength) { - labelMatch.push({ start: 0, end: h.end - descriptionPrefixLength }); - descriptionMatch.push({ start: h.start, end: descriptionPrefixLength }); - } - - // Match on label part - else if (h.start >= descriptionPrefixLength) { - labelMatch.push({ start: h.start - descriptionPrefixLength, end: h.end - descriptionPrefixLength }); - } - - // Match on description part - else { - descriptionMatch.push(h); - } - }); - - return { score: labelDescriptionScore, labelMatch, descriptionMatch }; - } - } - - 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); - - const scoreA = itemScoreA.score; - const scoreB = itemScoreB.score; - - // 1.) prefer identity matches - 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 - if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) { - if (scoreB < LABEL_SCORE_THRESHOLD) { - return -1; - } - - if (scoreA < LABEL_SCORE_THRESHOLD) { - return 1; - } - } - - // 5.) compare by score - if (scoreA !== scoreB) { - return scoreA > scoreB ? -1 : 1; - } - - // 6.) 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); -} - -function computeLabelAndDescriptionMatchDistance(item: T, score: IItemScore, accessor: IItemAccessor): number { - let matchStart: number = -1; - let matchEnd: number = -1; - - // If we have description matches, the start is first of description match - if (score.descriptionMatch && score.descriptionMatch.length) { - matchStart = score.descriptionMatch[0].start; - } - - // Otherwise, the start is the first label match - else if (score.labelMatch && score.labelMatch.length) { - matchStart = score.labelMatch[0].start; - } - - // If we have label match, the end is the last label match - // If we had a description match, we add the length of the description - // as offset to the end to indicate this. - if (score.labelMatch && score.labelMatch.length) { - matchEnd = score.labelMatch[score.labelMatch.length - 1].end; - if (score.descriptionMatch && score.descriptionMatch.length) { - const itemDescription = accessor.getItemDescription(item); - if (itemDescription) { - matchEnd += itemDescription.length; - } - } - } - - // If we have just a description match, the end is the last description match - else if (score.descriptionMatch && score.descriptionMatch.length) { - matchEnd = score.descriptionMatch[score.descriptionMatch.length - 1].end; - } - - return matchEnd - matchStart; -} - -function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number { - if ((!matchesA && !matchesB) || ((!matchesA || !matchesA.length) && (!matchesB || !matchesB.length))) { - return 0; // make sure to not cause bad comparing when matches are not provided - } - - if (!matchesB || !matchesB.length) { - return -1; - } - - if (!matchesA || !matchesA.length) { - return 1; - } - - // Compute match length of A (first to last match) - const matchStartA = matchesA[0].start; - const matchEndA = matchesA[matchesA.length - 1].end; - const matchLengthA = matchEndA - matchStartA; - - // Compute match length of B (first to last match) - const matchStartB = matchesB[0].start; - const matchEndB = matchesB[matchesB.length - 1].end; - const matchLengthB = matchEndB - matchStartB; - - // Prefer shorter match length - return matchLengthA === matchLengthB ? 0 : matchLengthB < matchLengthA ? 1 : -1; -} - -export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor): number { - - // check for label + description length and prefer shorter - const labelA = accessor.getItemLabel(itemA) || ''; - const labelB = accessor.getItemLabel(itemB) || ''; - - const descriptionA = accessor.getItemDescription(itemA); - const descriptionB = accessor.getItemDescription(itemB); - - const labelDescriptionALength = labelA.length + (descriptionA ? descriptionA.length : 0); - const labelDescriptionBLength = labelB.length + (descriptionB ? descriptionB.length : 0); - - if (labelDescriptionALength !== labelDescriptionBLength) { - return labelDescriptionALength - labelDescriptionBLength; - } - - // check for path length and prefer shorter - const pathA = accessor.getItemPath(itemA); - const pathB = accessor.getItemPath(itemB); - - if (pathA && pathB && pathA.length !== pathB.length) { - return pathA.length - pathB.length; - } - - // 7.) finally we have equal scores and equal length, we fallback to comparer - - // compare by label - if (labelA !== labelB) { - return compareAnything(labelA, labelB, query.value); - } - - // compare by description - if (descriptionA && descriptionB && descriptionA !== descriptionB) { - return compareAnything(descriptionA, descriptionB, query.value); - } - - // compare by path - if (pathA && pathB && pathA !== pathB) { - return compareAnything(pathA, pathB, query.value); - } - - // equal - return 0; -} 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 f53d0b4e1edc5..0000000000000 --- 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', () => { - 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)); - - ds.getChildren(null!, model).then((children: any[]) => { - assert.equal(3, children.length); - }); - }); -}); \ No newline at end of file diff --git a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts b/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts deleted file mode 100644 index 548cc9489f0b1..0000000000000 --- a/src/vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts +++ /dev/null @@ -1,836 +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 scorer from 'vs/base/parts/quickopen/common/quickOpenScorer'; -import { URI } from 'vs/base/common/uri'; -import { basename, dirname, sep } from 'vs/base/common/path'; -import { isWindows } from 'vs/base/common/platform'; - -class ResourceAccessorClass implements scorer.IItemAccessor { - - getItemLabel(resource: URI): string { - return basename(resource.fsPath); - } - - getItemDescription(resource: URI): string { - return dirname(resource.fsPath); - } - - getItemPath(resource: URI): string { - return resource.fsPath; - } -} - -const ResourceAccessor = new ResourceAccessorClass(); - -class NullAccessorClass implements scorer.IItemAccessor { - - getItemLabel(resource: URI): string { - return undefined!; - } - - getItemDescription(resource: URI): string { - return undefined!; - } - - getItemPath(resource: URI): string { - return undefined!; - } -} - -function _doScore(target: string, query: string, fuzzy: boolean): scorer.Score { - return scorer.score(target, query, query.toLowerCase(), 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 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); -} - -const NullAccessor = new NullAccessorClass(); -let cache: scorer.ScorerCache = Object.create(null); - -suite('Quick Open Scorer', () => { - - setup(() => { - cache = Object.create(null); - }); - - test('score (fuzzy)', function () { - const target = 'HeLlo-World'; - - const scores: scorer.Score[] = []; - 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, '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 - - // Assert scoring order - let sortedScores = scores.concat().sort((a, b) => b[0] - a[0]); - assert.deepEqual(scores, sortedScores); - - // Assert scoring positions - 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); - }); - - test('score (non fuzzy)', function () { - const target = 'HeLlo-World'; - - assert.ok(_doScore(target, 'HelLo-World', false)[0] > 0); - assert.equal(_doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length); - - assert.ok(_doScore(target, 'hello-world', false)[0] > 0); - assert.equal(_doScore(target, 'HW', false)[0], 0); - assert.ok(_doScore(target, 'h', false)[0] > 0); - assert.ok(_doScore(target, 'ello', false)[0] > 0); - assert.ok(_doScore(target, 'ld', false)[0] > 0); - assert.equal(_doScore(target, 'eo', false)[0], 0); - }); - - test('scoreItem - matches are proper', function () { - let res = scoreItem(null, 'something', true, ResourceAccessor, cache); - assert.ok(!res.score); - - const resource = URI.file('/xyz/some/path/someFile123.txt'); - - res = scoreItem(resource, 'something', true, NullAccessor, cache); - assert.ok(!res.score); - - // Path Identity - const identityRes = scoreItem(resource, ResourceAccessor.getItemPath(resource), true, ResourceAccessor, cache); - assert.ok(identityRes.score); - assert.equal(identityRes.descriptionMatch!.length, 1); - assert.equal(identityRes.labelMatch!.length, 1); - assert.equal(identityRes.descriptionMatch![0].start, 0); - assert.equal(identityRes.descriptionMatch![0].end, ResourceAccessor.getItemDescription(resource).length); - assert.equal(identityRes.labelMatch![0].start, 0); - assert.equal(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length); - - // Basename Prefix - const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor, cache); - assert.ok(basenamePrefixRes.score); - assert.ok(!basenamePrefixRes.descriptionMatch); - assert.equal(basenamePrefixRes.labelMatch!.length, 1); - assert.equal(basenamePrefixRes.labelMatch![0].start, 0); - assert.equal(basenamePrefixRes.labelMatch![0].end, 'som'.length); - - // Basename Camelcase - const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor, cache); - assert.ok(basenameCamelcaseRes.score); - assert.ok(!basenameCamelcaseRes.descriptionMatch); - assert.equal(basenameCamelcaseRes.labelMatch!.length, 2); - assert.equal(basenameCamelcaseRes.labelMatch![0].start, 0); - assert.equal(basenameCamelcaseRes.labelMatch![0].end, 1); - assert.equal(basenameCamelcaseRes.labelMatch![1].start, 4); - assert.equal(basenameCamelcaseRes.labelMatch![1].end, 5); - - // Basename Match - const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor, cache); - assert.ok(basenameRes.score); - assert.ok(!basenameRes.descriptionMatch); - assert.equal(basenameRes.labelMatch!.length, 2); - assert.equal(basenameRes.labelMatch![0].start, 1); - assert.equal(basenameRes.labelMatch![0].end, 2); - assert.equal(basenameRes.labelMatch![1].start, 4); - assert.equal(basenameRes.labelMatch![1].end, 5); - - // Path Match - const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor, cache); - assert.ok(pathRes.score); - assert.ok(pathRes.descriptionMatch); - assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch!.length, 1); - assert.equal(pathRes.labelMatch![0].start, 8); - assert.equal(pathRes.labelMatch![0].end, 11); - assert.equal(pathRes.descriptionMatch!.length, 1); - assert.equal(pathRes.descriptionMatch![0].start, 1); - assert.equal(pathRes.descriptionMatch![0].end, 4); - - // No Match - const noRes = scoreItem(resource, '987', true, ResourceAccessor, cache); - assert.ok(!noRes.score); - assert.ok(!noRes.labelMatch); - assert.ok(!noRes.descriptionMatch); - - // Verify Scores - assert.ok(identityRes.score > basenamePrefixRes.score); - assert.ok(basenamePrefixRes.score > basenameRes.score); - assert.ok(basenameRes.score > pathRes.score); - assert.ok(pathRes.score > noRes.score); - }); - - test('scoreItem - invalid input', function () { - - let res = scoreItem(null, null!, true, ResourceAccessor, cache); - assert.equal(res.score, 0); - - res = scoreItem(null, 'null', true, ResourceAccessor, cache); - assert.equal(res.score, 0); - }); - - test('scoreItem - optimize for file paths', function () { - const resource = URI.file('/xyz/others/spath/some/xsp/file123.txt'); - - // 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); - assert.ok(pathRes.score); - assert.ok(pathRes.descriptionMatch); - assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch!.length, 1); - assert.equal(pathRes.labelMatch![0].start, 0); - assert.equal(pathRes.labelMatch![0].end, 7); - assert.equal(pathRes.descriptionMatch!.length, 1); - assert.equal(pathRes.descriptionMatch![0].start, 23); - assert.equal(pathRes.descriptionMatch![0].end, 26); - }); - - 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); - assert.ok(pathRes.score); - assert.ok(pathRes.descriptionMatch); - assert.ok(pathRes.labelMatch); - assert.equal(pathRes.labelMatch!.length, 1); - assert.equal(pathRes.labelMatch![0].start, 0); - assert.equal(pathRes.labelMatch![0].end, 9); - }); - - test('scoreItem - prefers more compact matches', function () { - const resource = URI.file('/1a111d1/11a1d1/something.txt'); - - // 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); - assert.ok(res.score); - assert.ok(res.descriptionMatch); - assert.ok(!res.labelMatch!.length); - assert.equal(res.descriptionMatch!.length, 2); - assert.equal(res.descriptionMatch![0].start, 11); - assert.equal(res.descriptionMatch![0].end, 12); - assert.equal(res.descriptionMatch![1].start, 13); - assert.equal(res.descriptionMatch![1].end, 14); - }); - - test('scoreItem - proper target offset', function () { - const resource = URI.file('etem'); - - const res = scoreItem(resource, 'teem', true, ResourceAccessor, cache); - assert.ok(!res.score); - }); - - test('scoreItem - proper target offset #2', function () { - const resource = URI.file('ede'); - - const res = scoreItem(resource, 'de', true, ResourceAccessor, cache); - - assert.equal(res.labelMatch!.length, 1); - assert.equal(res.labelMatch![0].start, 1); - assert.equal(res.labelMatch![0].end, 3); - }); - - 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); - - assert.equal(res.descriptionMatch!.length, 3); - assert.equal(res.descriptionMatch![0].start, 9); - assert.equal(res.descriptionMatch![0].end, 10); - assert.equal(res.descriptionMatch![1].start, 36); - assert.equal(res.descriptionMatch![1].end, 37); - assert.equal(res.descriptionMatch![2].start, 40); - assert.equal(res.descriptionMatch![2].end, 41); - - assert.equal(res.labelMatch!.length, 2); - assert.equal(res.labelMatch![0].start, 9); - assert.equal(res.labelMatch![0].end, 10); - assert.equal(res.labelMatch![1].start, 20); - assert.equal(res.labelMatch![1].end, 21); - }); - - test('scoreItem - no match unless query contained in sequence', function () { - const resource = URI.file('abcde'); - - const res = scoreItem(resource, 'edcda', true, ResourceAccessor, cache); - 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'); - const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); - - // Full resource A path - let query = ResourceAccessor.getItemPath(resourceA); - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceB); - assert.equal(res[2], resourceC); - - // Full resource B path - query = ResourceAccessor.getItemPath(resourceB); - - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - assert.equal(res[2], resourceC); - }); - - test('compareFilesByScore - basename prefix', function () { - const resourceA = URI.file('/some/path/fileA.txt'); - const resourceB = URI.file('/some/path/other/fileB.txt'); - const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); - - // Full resource A basename - let query = ResourceAccessor.getItemLabel(resourceA); - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceB); - assert.equal(res[2], resourceC); - - // Full resource B basename - query = ResourceAccessor.getItemLabel(resourceB); - - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - assert.equal(res[2], resourceC); - }); - - test('compareFilesByScore - basename camelcase', function () { - const resourceA = URI.file('/some/path/fileA.txt'); - const resourceB = URI.file('/some/path/other/fileB.txt'); - const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); - - // resource A camelcase - let query = 'fA'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceB); - assert.equal(res[2], resourceC); - - // resource B camelcase - query = 'fB'; - - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - assert.equal(res[2], resourceC); - }); - - test('compareFilesByScore - basename scores', function () { - const resourceA = URI.file('/some/path/fileA.txt'); - const resourceB = URI.file('/some/path/other/fileB.txt'); - const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); - - // Resource A part of basename - let query = 'fileA'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceB); - assert.equal(res[2], resourceC); - - // Resource B part of basename - query = 'fileB'; - - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - assert.equal(res[2], resourceC); - }); - - test('compareFilesByScore - path scores', function () { - const resourceA = URI.file('/some/path/fileA.txt'); - const resourceB = URI.file('/some/path/other/fileB.txt'); - const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); - - // Resource A part of path - let query = 'pathfileA'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceB); - assert.equal(res[2], resourceC); - - // Resource B part of path - query = 'pathfileB'; - - res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - assert.equal(res[2], resourceC); - }); - - test('compareFilesByScore - prefer shorter basenames', function () { - const resourceA = URI.file('/some/path/fileA.txt'); - const resourceB = URI.file('/some/path/other/fileBLonger.txt'); - const resourceC = URI.file('/unrelated/the/path/other/fileC.txt'); - - // Resource A part of path - let query = 'somepath'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceB); - assert.equal(res[2], resourceC); - }); - - test('compareFilesByScore - prefer shorter basenames (match on basename)', function () { - const resourceA = URI.file('/some/path/fileA.txt'); - const resourceB = URI.file('/some/path/other/fileBLonger.txt'); - const resourceC = URI.file('/unrelated/the/path/other/fileC.txt'); - - // Resource A part of path - let query = 'file'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceC); - assert.equal(res[2], resourceB); - }); - - test('compareFilesByScore - prefer shorter paths', function () { - const resourceA = URI.file('/some/path/fileA.txt'); - const resourceB = URI.file('/some/path/other/fileB.txt'); - const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); - - // Resource A part of path - let query = 'somepath'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceB); - assert.equal(res[2], resourceC); - }); - - test('compareFilesByScore - prefer shorter paths (bug #17443)', function () { - const resourceA = URI.file('config/test/t1.js'); - const resourceB = URI.file('config/test.js'); - const resourceC = URI.file('config/test/t2.js'); - - let query = 'co/te'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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'); - - let query = 'vscode'; - - 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; - }); - }); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - }); - - 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)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - }); - - test('compareFilesByScore - prefer more compact matches (label)', function () { - const resourceA = URI.file('config/test/examasdaple.js'); - const resourceB = URI.file('config/test/exampleasdaasd.ts'); - - let query = 'xp'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - }); - - test('compareFilesByScore - prefer more compact matches (path)', function () { - const resourceA = URI.file('config/test/examasdaple/file.js'); - const resourceB = URI.file('config/test/exampleasdaasd/file.ts'); - - let query = 'xp'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - }); - - test('compareFilesByScore - prefer more compact matches (label and path)', function () { - const resourceA = URI.file('config/example/thisfile.ts'); - const resourceB = URI.file('config/24234243244/example/file.js'); - - let query = 'exfile'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - assert.equal(res[1], resourceA); - }); - - test('compareFilesByScore - avoid match scattering (bug #34210)', function () { - const resourceA = URI.file('node_modules1/bundle/lib/model/modules/ot1/index.js'); - const resourceB = URI.file('node_modules1/bundle/lib/model/modules/un1/index.js'); - const resourceC = URI.file('node_modules1/bundle/lib/model/modules/modu1/index.js'); - const resourceD = URI.file('node_modules1/bundle/lib/model/modules/oddl1/index.js'); - - 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)); - assert.equal(res[0], resourceC); - - res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceB); - - res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #21019 1.)', function () { - const resourceA = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceLoad/index.js'); - const resourceB = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceDistribution/index.js'); - const resourceC = URI.file('app/containers/Services/NetworkData/ServiceDetailTabs/ServiceTabs/StatVideo/index.js'); - - let query = 'StatVideoindex'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceC); - - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceC); - }); - - test('compareFilesByScore - avoid match scattering (bug #21019 2.)', function () { - const resourceA = URI.file('src/build-helper/store/redux.ts'); - const resourceB = URI.file('src/repository/store/redux.ts'); - - let query = 'reproreduxts'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #26649)', function () { - const resourceA = URI.file('photobook/src/components/AddPagesButton/index.js'); - const resourceB = URI.file('photobook/src/components/ApprovalPageHeader/index.js'); - const resourceC = URI.file('photobook/src/canvasComponents/BookPage/index.js'); - - let query = 'bookpageIndex'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceC); - - res = [resourceC, resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceC); - }); - - test('compareFilesByScore - avoid match scattering (bug #33247)', function () { - const resourceA = URI.file('ui/src/utils/constants.js'); - const resourceB = URI.file('ui/src/ui/Icons/index.js'); - - let query = isWindows ? 'ui\\icons' : 'ui/icons'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #33247 comment)', function () { - const resourceA = URI.file('ui/src/components/IDInput/index.js'); - const resourceB = URI.file('ui/src/ui/Input/index.js'); - - let query = isWindows ? 'ui\\input\\index' : 'ui/input/index'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #36166)', function () { - const resourceA = URI.file('django/contrib/sites/locale/ga/LC_MESSAGES/django.mo'); - const resourceB = URI.file('django/core/signals.py'); - - let query = 'djancosig'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #32918)', function () { - const resourceA = URI.file('adsys/protected/config.php'); - const resourceB = URI.file('adsys/protected/framework/smarty/sysplugins/smarty_internal_config.php'); - const resourceC = URI.file('duowanVideo/wap/protected/config.php'); - - let query = 'protectedconfig.php'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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)); - assert.equal(res[0], resourceA); - assert.equal(res[1], resourceC); - assert.equal(res[2], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #14879)', function () { - const resourceA = URI.file('pkg/search/gradient/testdata/constraint_attrMatchString.yml'); - const resourceB = URI.file('cmd/gradient/main.go'); - - let query = 'gradientmain'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #14727 1)', function () { - const resourceA = URI.file('alpha-beta-cappa.txt'); - const resourceB = URI.file('abc.txt'); - - let query = 'abc'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #14727 2)', function () { - const resourceA = URI.file('xerxes-yak-zubba/index.js'); - const resourceB = URI.file('xyz/index.js'); - - let query = 'xyz'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #18381)', function () { - const resourceA = URI.file('AssymblyInfo.cs'); - const resourceB = URI.file('IAsynchronousTask.java'); - - let query = 'async'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #35572)', function () { - const resourceA = URI.file('static/app/source/angluar/-admin/-organization/-settings/layout/layout.js'); - const resourceB = URI.file('static/app/source/angular/-admin/-project/-settings/_settings/settings.js'); - - let query = 'partisettings'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #36810)', function () { - const resourceA = URI.file('Trilby.TrilbyTV.Web.Portal/Views/Systems/Index.cshtml'); - const resourceB = URI.file('Trilby.TrilbyTV.Web.Portal/Areas/Admins/Views/Tips/Index.cshtml'); - - let query = 'tipsindex.cshtml'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - prefer shorter hit (bug #20546)', function () { - const resourceA = URI.file('editor/core/components/tests/list-view-spec.js'); - const resourceB = URI.file('editor/core/components/list-view.js'); - - let query = 'listview'; - - let res = [resourceA, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceB, resourceA].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - }); - - test('compareFilesByScore - avoid match scattering (bug #12095)', function () { - const resourceA = URI.file('src/vs/workbench/contrib/files/common/explorerViewModel.ts'); - const resourceB = URI.file('src/vs/workbench/contrib/files/browser/views/explorerView.ts'); - const resourceC = URI.file('src/vs/workbench/contrib/files/browser/views/explorerViewer.ts'); - - let query = 'filesexplorerview.ts'; - - let res = [resourceA, resourceB, resourceC].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - assert.equal(res[0], resourceB); - - res = [resourceA, resourceC, resourceB].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor, cache)); - 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'); - assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false); - assert.equal(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true); - }); -}); \ No newline at end of file diff --git a/src/vs/base/parts/request/browser/request.ts b/src/vs/base/parts/request/browser/request.ts index f8532bbe6085e..1c2499ba404d8 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 0000000000000..c7729f338afad --- /dev/null +++ b/src/vs/base/parts/sandbox/common/electronTypes.ts @@ -0,0 +1,311 @@ +/*--------------------------------------------------------------------------------------------- + * 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 7.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; + /** + * 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'>; + /** + * 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`, `alt`, `meta`, + * `isKeypad`, `isAutoRepeat`, `leftButtonDown`, `middleButtonDown`, + * `rightButtonDown`, `capsLock`, `numLock`, `left`, `right`. + */ + modifiers: Array<'shift' | 'control' | 'alt' | 'meta' | '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; +} + +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; +} 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 0000000000000..18fb9d624229d --- /dev/null +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * 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'); + + 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: { + platform: process.platform, + env: process.env, + versions: process.versions, + _whenEnvResolved: undefined, + get whenEnvResolved() { + if (!this._whenEnvResolved) { + this._whenEnvResolved = resolveEnv(); + } + + return this._whenEnvResolved; + }, + 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: { + sandbox: 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/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts new file mode 100644 index 0000000000000..0c77d5dc453a6 --- /dev/null +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const ipcRenderer = (window as any).vscode.ipcRenderer as { + + /** + * 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 const webFrame = (window as any).vscode.webFrame as { + + /** + * 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 const crashReporter = (window as any).vscode.crashReporter as { + + /** + * 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 const process = (window as any).vscode.process as { + + /** + * 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 process.env property returns an object containing the user environment. See environ(7). + */ + env: { [key: string]: string | undefined }; + + /** + * 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; + + /** + * A listener on the process. Only a small subset of listener types are allowed. + */ + on: (type: string, callback: Function) => void; + + /** + * A list of versions for the current node.js/electron configuration. + */ + versions: { [key: string]: string | undefined }; +}; + +export const context = (window as any).vscode.context as { + + /** + * Wether the renderer runs with `sandbox` enabled or not. + */ + sandbox: boolean; +}; 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 0000000000000..ac3e651171071 --- /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 03dedeca57f45..b6602b6a626af 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 { @@ -73,26 +74,24 @@ 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(); 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 +103,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 +144,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 +153,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(); @@ -294,13 +294,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 +308,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 e73aafe6ad4c0..d6eca3ac03f00 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 logger: SQLiteStorageDatabaseLogger; + private readonly name = basename(this.path); - private whenConnected: Promise; + private readonly logger = new SQLiteStorageDatabaseLogger(this.options.logging); - constructor(path: string, options: ISQLiteStorageDatabaseOptions = Object.create(null)) { - this.path = path; - this.name = basename(path); + private readonly whenConnected = this.connect(this.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 92992e494b01c..5c25b92dffc56 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -124,28 +124,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(); @@ -294,7 +293,7 @@ suite('SQLite Storage Library', () => { return set; } - async function testDBBasics(path: string, logError?: (error: Error) => void) { + async function testDBBasics(path: string, logError?: (error: Error | string) => void) { let options!: ISQLiteStorageDatabaseOptions; if (logError) { options = { @@ -550,7 +549,7 @@ suite('SQLite Storage Library', () => { let storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); const items1 = new Map(); - items1.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#09885a"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","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":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#09885a"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#09885a"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#09885a"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#09885a"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); + items1.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#098658"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","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":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#098658"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#098658"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#098658"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#098658"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); items1.set('commandpalette.mru.cache', '{"usesLRU":true,"entries":[{"key":"revealFileInOS","value":3},{"key":"extension.openInGitHub","value":4},{"key":"workbench.extensions.action.openExtensionsFolder","value":11},{"key":"workbench.action.showRuntimeExtensions","value":14},{"key":"workbench.action.toggleTabsVisibility","value":15},{"key":"extension.liveServerPreview.open","value":16},{"key":"workbench.action.openIssueReporter","value":18},{"key":"workbench.action.openProcessExplorer","value":19},{"key":"workbench.action.toggleSharedProcess","value":20},{"key":"workbench.action.configureLocale","value":21},{"key":"workbench.action.appPerf","value":22},{"key":"workbench.action.reportPerformanceIssueUsingReporter","value":23},{"key":"workbench.action.openGlobalKeybindings","value":25},{"key":"workbench.action.output.toggleOutput","value":27},{"key":"extension.sayHello","value":29}]}'); items1.set('cpp.1.lastsessiondate', 'Fri Oct 05 2018'); items1.set('debug.actionswidgetposition', '0.6880952380952381'); @@ -641,7 +640,7 @@ suite('SQLite Storage Library', () => { let storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); const items = new Map(); - items.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#09885a"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","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":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#09885a"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#09885a"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#09885a"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#09885a"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); + items.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#098658"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","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":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#098658"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#098658"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#098658"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#098658"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); items.set('commandpalette.mru.cache', '{"usesLRU":true,"entries":[{"key":"revealFileInOS","value":3},{"key":"extension.openInGitHub","value":4},{"key":"workbench.extensions.action.openExtensionsFolder","value":11},{"key":"workbench.action.showRuntimeExtensions","value":14},{"key":"workbench.action.toggleTabsVisibility","value":15},{"key":"extension.liveServerPreview.open","value":16},{"key":"workbench.action.openIssueReporter","value":18},{"key":"workbench.action.openProcessExplorer","value":19},{"key":"workbench.action.toggleSharedProcess","value":20},{"key":"workbench.action.configureLocale","value":21},{"key":"workbench.action.appPerf","value":22},{"key":"workbench.action.reportPerformanceIssueUsingReporter","value":23},{"key":"workbench.action.openGlobalKeybindings","value":25},{"key":"workbench.action.output.toggleOutput","value":27},{"key":"extension.sayHello","value":29}]}'); let uuid = generateUuid(); 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 3e709334db6cc..0000000000000 --- 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 82b4015e6dd26..0000000000000 --- 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 f91ca2bcf84ce..0000000000000 --- 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 810e6ce98ee9c..0000000000000 --- 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 8b8f7b28078c7..0000000000000 --- 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 30df6dc07d788..0000000000000 --- 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/treeUtils.ts b/src/vs/base/parts/tree/browser/treeUtils.ts deleted file mode 100644 index 6d536a7fa5a9e..0000000000000 --- a/src/vs/base/parts/tree/browser/treeUtils.ts +++ /dev/null @@ -1,18 +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'; - -export function isEqualOrParent(tree: _.ITree, element: any, candidateParent: any): boolean { - const nav = tree.getNavigator(element); - - do { - if (element === candidateParent) { - return true; - } - } while (element = nav.parent()); - - return false; -} 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 64e5cfdb24551..0000000000000 --- 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 823f0bb0a9d14..0000000000000 --- 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 18d27eefada42..0000000000000 --- 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 97c2846ebb618..0000000000000 --- 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 0000000000000..049c4d48bb311 --- /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 0000000000000..fd5b0332a51ac --- /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 369f160803bcc..4c40ea2a065d7 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 61252159d230e..f5c8e65dac66b 100644 --- a/src/vs/base/test/browser/dom.test.ts +++ b/src/vs/base/test/browser/dom.test.ts @@ -93,6 +93,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 +127,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 525eb86e741b8..78f82030b2c57 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -6,42 +6,113 @@ import * as assert from 'assert'; import * as marked from 'vs/base/common/marked/marked'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; +import { MarkdownString, IMarkdownString } from 'vs/base/common/htmlContent'; +import { URI } from 'vs/base/common/uri'; +import { parse } from 'vs/base/common/marshalling'; suite('MarkdownRenderer', () => { - test('image rendering conforms to default', () => { - const markdown = { value: `![image](someimageurl 'caption')` }; - const result: HTMLElement = renderMarkdown(markdown); - const renderer = new marked.Renderer(); - const imageFromMarked = marked(markdown.value, { - sanitize: true, - renderer - }).trim(); - assert.strictEqual(result.innerHTML, imageFromMarked); - }); + suite('Images', () => { + + test('image rendering conforms to default', () => { + const markdown = { value: `![image](someimageurl 'caption')` }; + const result: HTMLElement = renderMarkdown(markdown); + const renderer = new marked.Renderer(); + const imageFromMarked = marked(markdown.value, { + renderer + }).trim(); + assert.strictEqual(result.innerHTML, imageFromMarked); + }); + + test('image rendering conforms to default without title', () => { + const markdown = { value: `![image](someimageurl)` }; + const result: HTMLElement = renderMarkdown(markdown); + const renderer = new marked.Renderer(); + const imageFromMarked = marked(markdown.value, { + renderer + }).trim(); + assert.strictEqual(result.innerHTML, imageFromMarked); + }); + + test('image width from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

    image

    `); + }); + + test('image height from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

    image

    `); + }); + + test('image width and height from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

    image

    `); + }); - test('image rendering conforms to default without title', () => { - const markdown = { value: `![image](someimageurl)` }; - const result: HTMLElement = renderMarkdown(markdown); - const renderer = new marked.Renderer(); - const imageFromMarked = marked(markdown.value, { - sanitize: true, - renderer - }).trim(); - assert.strictEqual(result.innerHTML, imageFromMarked); }); - test('image width from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

    image

    `); + suite('ThemeIcons Support On', () => { + + test('render appendText', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendText('$(zap) $(not a theme icon) $(add)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

    $(zap) $(not a theme icon) $(add)

    `); + }); + + test('render appendMarkdown', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown('$(zap) $(not a theme icon) $(add)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

    $(not a theme icon)

    `); + }); + + test('render appendMarkdown with escaped icon', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

    $(zap) $(not a theme icon)

    `); + }); + }); - test('image height from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

    image

    `); + suite('ThemeIcons Support Off', () => { + + test('render appendText', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: false }); + mds.appendText('$(zap) $(not a theme icon) $(add)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

    $(zap) $(not a theme icon) $(add)

    `); + }); + + test('render appendMarkdown with escaped icon', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: false }); + mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

    $(zap) $(not a theme icon) $(add)

    `); + }); + }); - test('image width and height from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

    image

    `); + 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/progressBar.test.ts b/src/vs/base/test/browser/progressBar.test.ts index 03e0a061419db..f43082a0bc677 100644 --- a/src/vs/base/test/browser/progressBar.test.ts +++ b/src/vs/base/test/browser/progressBar.test.ts @@ -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. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; @@ -28,4 +29,4 @@ suite('ProgressBar', () => { bar.dispose(); }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/browser/ui/grid/grid.test.ts b/src/vs/base/test/browser/ui/grid/grid.test.ts index a228eafffd86c..7956239d44d99 100644 --- a/src/vs/base/test/browser/ui/grid/grid.test.ts +++ b/src/vs/base/test/browser/ui/grid/grid.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { Direction, getRelativeLocation, Orientation, SerializableGrid, ISerializableView, IViewDeserializer, GridNode, Sizing, isGridBranchNode, sanitizeGridNodeDescriptor, GridNodeDescriptor, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; import { TestView, nodesToArrays } from './util'; import { deepClone } from 'vs/base/common/objects'; +import { Event } from 'vs/base/common/event'; // Simple example: // @@ -786,7 +787,7 @@ suite('SerializableGrid', function () { test('sanitizeGridNodeDescriptor', () => { const nodeDescriptor = { groups: [{ size: 0.2 }, { size: 0.2 }, { size: 0.6, groups: [{}, {}] }] }; const nodeDescriptorCopy = deepClone(nodeDescriptor); - sanitizeGridNodeDescriptor(nodeDescriptorCopy); + sanitizeGridNodeDescriptor(nodeDescriptorCopy, true); assert.deepEqual(nodeDescriptorCopy, { groups: [{ size: 0.2 }, { size: 0.2 }, { size: 0.6, groups: [{ size: 0.5 }, { size: 0.5 }] }] }); }); @@ -814,6 +815,33 @@ suite('SerializableGrid', function () { }); }); + test('createSerializedGrid - issue #85601, should not allow single children groups', () => { + const serializedGrid = createSerializedGrid({ orientation: Orientation.HORIZONTAL, groups: [{ groups: [{}, {}], size: 0.5 }, { groups: [{}], size: 0.5 }] }); + const views: ISerializableView[] = []; + const deserializer = new class implements IViewDeserializer { + fromJSON(): ISerializableView { + const view: ISerializableView = { + element: document.createElement('div'), + layout: () => null, + minimumWidth: 0, + maximumWidth: Number.POSITIVE_INFINITY, + minimumHeight: 0, + maximumHeight: Number.POSITIVE_INFINITY, + onDidChange: Event.None, + toJSON: () => ({}) + }; + views.push(view); + return view; + } + }; + + const grid = SerializableGrid.deserialize(serializedGrid, deserializer); + assert.equal(views.length, 3); + + // should not throw + grid.removeView(views[2]); + }); + test('serialize should store visibility and previous size', function () { const view1 = new TestSerializableView('view1', 50, Number.MAX_VALUE, 50, Number.MAX_VALUE); const grid = new SerializableGrid(view1); diff --git a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts index 9dad8183e9164..5ab03d9cd4ccb 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts @@ -8,10 +8,7 @@ import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; suite('ScrollbarState', () => { test('inflates slider size', () => { - let actual = new ScrollbarState(0, 14, 0); - actual.setVisibleSize(339); - actual.setScrollSize(42423); - actual.setScrollPosition(32787); + let actual = new ScrollbarState(0, 14, 0, 339, 42423, 32787); assert.equal(actual.getArrowSize(), 0); assert.equal(actual.getScrollPosition(), 32787); @@ -34,10 +31,7 @@ suite('ScrollbarState', () => { }); test('inflates slider size with arrows', () => { - let actual = new ScrollbarState(12, 14, 0); - actual.setVisibleSize(339); - actual.setScrollSize(42423); - actual.setScrollPosition(32787); + let actual = new ScrollbarState(12, 14, 0, 339, 42423, 32787); assert.equal(actual.getArrowSize(), 12); assert.equal(actual.getScrollPosition(), 32787); 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 264a2bac86b96..2620db0b7d86e 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -8,7 +8,7 @@ import { Emitter } from 'vs/base/common/event'; import { SplitView, IView, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; import { Sash, SashState } from 'vs/base/browser/ui/sash/sash'; -class TestView implements IView { +class TestView implements IView { private readonly _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; @@ -43,7 +43,7 @@ class TestView implements IView { assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size'); } - layout(size: number, orthogonalSize: number | undefined): void { + layout(size: number, _offset: number, orthogonalSize: number | undefined): void { this._size = size; this._orthogonalSize = orthogonalSize; this._onDidLayout.fire({ size, orthogonalSize }); @@ -527,11 +527,11 @@ suite('Splitview', () => { view1.dispose(); }); - test('orthogonal size propagates to views', () => { + test('context propagates to views', () => { const view1 = new TestView(20, Number.POSITIVE_INFINITY); const view2 = new TestView(20, Number.POSITIVE_INFINITY); const view3 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low); - const splitview = new SplitView(container, { proportionalLayout: false }); + const splitview = new SplitView(container, { proportionalLayout: false }); splitview.layout(200); splitview.addView(view1, Sizing.Distribute); 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 1177889e12af8..8d66a9b360724 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -7,24 +7,32 @@ 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 { id: string; + suffix?: string; children?: Element[]; } -function find(elements: Element[] | undefined, id: string): Element { - while (elements) { - for (const element of elements) { - if (element.id === id) { - return element; - } +function find(element: Element, id: string): Element | undefined { + if (element.id === id) { + return element; + } + + if (!element.children) { + return undefined; + } + + for (const child of element.children) { + const result = find(child, id); + + if (result) { + return result; } } - throw new Error('element not found'); + return undefined; } class Renderer implements ITreeRenderer { @@ -33,7 +41,7 @@ class Renderer implements ITreeRenderer { return container; } renderElement(element: ITreeNode, index: number, templateData: HTMLElement): void { - templateData.textContent = element.element.id; + templateData.textContent = element.element.id + (element.element.suffix || ''); } disposeTemplate(templateData: HTMLElement): void { // noop @@ -65,7 +73,13 @@ class Model { constructor(readonly root: Element) { } get(id: string): Element { - return find(this.root.children, id); + const result = find(this.root, id); + + if (!result) { + throw new Error('element not found'); + } + + return result; } } @@ -88,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' }, @@ -136,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' }]; @@ -145,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 = []; @@ -154,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' }]; @@ -163,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); }); @@ -226,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')); @@ -235,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); }); @@ -280,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 || []))); } }; @@ -323,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 || []))); } }; @@ -372,21 +386,53 @@ 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 () => { + const container = document.createElement('div'); + const model = new Model({ + id: 'root', + children: [{ + id: 'a', + children: [{ + id: 'b', + suffix: '1' + }] + }] + }); + + const tree = new AsyncDataTree('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() }); + tree.layout(200); + + await tree.setInput(model.root); + await tree.expand(model.get('a')); + assert.deepEqual(Array.from(container.querySelectorAll('.monaco-list-row')).map(e => e.textContent), ['a', 'b1']); + + const a = model.get('a'); + const b = model.get('b'); + a.children?.splice(0, 1, { id: 'b', suffix: '2' }); + + await Promise.all([ + tree.updateChildren(a, true, true), + tree.updateChildren(b, true, true) + ]); + + assert.deepEqual(Array.from(container.querySelectorAll('.monaco-list-row')).map(e => e.textContent), ['a', 'b2']); }); }); 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 b69e61abcb826..18eb84d51831c 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 d1914e5584f5e..6c9dfeae8ea5a 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 d384c368d6ee3..166a99c00b34a 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 66ecd17238f0f..4663962b557be 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 4c0fe3e3e17c3..288bd265f8a10 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -342,5 +342,14 @@ 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/assert.test.ts b/src/vs/base/test/common/assert.test.ts index c7d3343ba2934..a925cd0437aec 100644 --- a/src/vs/base/test/common/assert.test.ts +++ b/src/vs/base/test/common/assert.test.ts @@ -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. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { ok } from 'vs/base/common/assert'; diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 586c0fa8a7f13..aa055d77c6719 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,152 @@ 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'); + }); }); diff --git a/src/vs/base/test/common/codicon.test.ts b/src/vs/base/test/common/codicon.test.ts index b3fdb5bde1bef..8d974ea0ad4e4 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/collections.test.ts b/src/vs/base/test/common/collections.test.ts index 353b5d0147d8a..e5449f4633398 100644 --- a/src/vs/base/test/common/collections.test.ts +++ b/src/vs/base/test/common/collections.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import * as collections from 'vs/base/common/collections'; - suite('Collections', () => { test('forEach', () => { diff --git a/src/vs/base/test/common/decorators.test.ts b/src/vs/base/test/common/decorators.test.ts index e7882ddb4790c..9a01f60a3f478 100644 --- a/src/vs/base/test/common/decorators.test.ts +++ b/src/vs/base/test/common/decorators.test.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as sinon from 'sinon'; import * as assert from 'assert'; -import { memoize, createMemoizer } from 'vs/base/common/decorators'; +import { memoize, createMemoizer, throttle } from 'vs/base/common/decorators'; suite('Decorators', () => { test('memoize should memoize methods', () => { @@ -100,7 +101,9 @@ suite('Decorators', () => { test('memoized property should not be enumerable', () => { class Foo { @memoize - get answer() { return 42; } + get answer() { + return 42; + } } const foo = new Foo(); @@ -112,7 +115,9 @@ suite('Decorators', () => { test('memoized property should not be writable', () => { class Foo { @memoize - get answer() { return 42; } + get answer() { + return 42; + } } const foo = new Foo(); @@ -131,7 +136,9 @@ suite('Decorators', () => { let counter = 0; class Foo { @memoizer - get answer() { return ++counter; } + get answer() { + return ++counter; + } } const foo = new Foo(); @@ -145,4 +152,49 @@ suite('Decorators', () => { assert.equal(foo.answer, 3); assert.equal(foo.answer, 3); }); + + test('throttle', () => { + const spy = sinon.spy(); + const clock = sinon.useFakeTimers(); + try { + class ThrottleTest { + private _handle: Function; + + constructor(fn: Function) { + this._handle = fn; + } + + @throttle( + 100, + (a: number, b: number) => a + b, + () => 0 + ) + report(p: number): void { + this._handle(p); + } + } + + const t = new ThrottleTest(spy); + + t.report(1); + t.report(2); + t.report(3); + assert.deepEqual(spy.args, [[1]]); + + clock.tick(200); + assert.deepEqual(spy.args, [[1], [5]]); + spy.reset(); + + t.report(4); + t.report(5); + clock.tick(50); + t.report(6); + + assert.deepEqual(spy.args, [[4]]); + clock.tick(60); + assert.deepEqual(spy.args, [[4], [11]]); + } finally { + clock.restore(); + } + }); }); diff --git a/src/vs/base/test/common/errors.test.ts b/src/vs/base/test/common/errors.test.ts index d2dc4bfcb573f..a5e7916236166 100644 --- a/src/vs/base/test/common/errors.test.ts +++ b/src/vs/base/test/common/errors.test.ts @@ -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. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -16,9 +17,17 @@ suite('Errors', () => { error.detail.exception = {}; error.detail.exception.message = 'Foo Bar'; assert.strictEqual(toErrorMessage(error), 'Foo Bar'); + assert.strictEqual(toErrorMessage(error, true), 'Foo Bar'); assert(toErrorMessage()); assert(toErrorMessage(null)); assert(toErrorMessage({})); + + try { + throw new Error(); + } catch (error) { + assert.strictEqual(toErrorMessage(error), 'An unknown error occurred. Please consult the log for more details.'); + assert.ok(toErrorMessage(error, true).length > 'An unknown error occurred. Please consult the log for more details.'.length); + } }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 25d47dc6e00a8..bb589ca763303 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Event, Emitter, EventBufferer, EventMultiplexer, AsyncEmitter, IWaitUntil, PauseableEmitter } from 'vs/base/common/event'; +import { Event, Emitter, EventBufferer, EventMultiplexer, IWaitUntil, PauseableEmitter, AsyncEmitter } from 'vs/base/common/event'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as Errors from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; namespace Samples { @@ -135,6 +136,7 @@ suite('Event', function () { let a = new Emitter(); let hit = false; a.event(function () { + // eslint-disable-next-line no-throw-literal throw 9; }); a.event(function () { @@ -174,7 +176,7 @@ suite('Event', function () { test('Debounce Event', function (done: () => void) { let doc = new Samples.Document3(); - let onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[], cur) => { + let onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[] | undefined, cur) => { if (!prev) { prev = [cur]; } else if (prev.indexOf(cur) < 0) { @@ -235,6 +237,20 @@ suite('Event', function () { assert.equal(calls, 2); }); + test('Debounce Event - leading reset', async function () { + const emitter = new Emitter(); + let debounced = Event.debounce(emitter.event, (l, e) => l ? l + 1 : 1, 0, /*leading=*/true); + + let calls: number[] = []; + debounced((e) => calls.push(e)); + + emitter.fire(1); + emitter.fire(1); + + await timeout(1); + assert.deepEqual(calls, [1, 1]); + }); + test('Emitter - In Order Delivery', function () { const a = new Emitter(); const listener2Events: string[] = []; @@ -272,11 +288,7 @@ suite('AsyncEmitter', function () { assert.equal(typeof e.waitUntil, 'function'); }); - emitter.fireAsync(thenables => ({ - foo: true, - bar: 1, - waitUntil(t: Promise) { thenables.push(t); } - })); + emitter.fireAsync({ foo: true, bar: 1, }, CancellationToken.None); emitter.dispose(); }); @@ -303,12 +315,7 @@ suite('AsyncEmitter', function () { })); }); - await emitter.fireAsync(thenables => ({ - foo: true, - waitUntil(t) { - thenables.push(t); - } - })); + await emitter.fireAsync({ foo: true }, CancellationToken.None); assert.equal(globalState, 2); }); @@ -324,12 +331,7 @@ suite('AsyncEmitter', function () { emitter.event(e => { e.waitUntil(timeout(10).then(async _ => { if (e.foo === 1) { - await emitter.fireAsync(thenables => ({ - foo: 2, - waitUntil(t) { - thenables.push(t); - } - })); + await emitter.fireAsync({ foo: 2 }, CancellationToken.None); assert.deepEqual(events, [1, 2]); done = true; } @@ -342,12 +344,7 @@ suite('AsyncEmitter', function () { e.waitUntil(timeout(7)); }); - await emitter.fireAsync(thenables => ({ - foo: 1, - waitUntil(t) { - thenables.push(t); - } - })); + await emitter.fireAsync({ foo: 1 }, CancellationToken.None); assert.ok(done); }); @@ -372,12 +369,7 @@ suite('AsyncEmitter', function () { e.waitUntil(timeout(10)); }); - await emitter.fireAsync(thenables => ({ - foo: true, - waitUntil(t) { - thenables.push(t); - } - })).then(() => { + await emitter.fireAsync({ foo: true }, CancellationToken.None).then(() => { assert.equal(globalState, 2); }).catch(e => { console.log(e); diff --git a/src/vs/base/test/common/extpath.test.ts b/src/vs/base/test/common/extpath.test.ts index da6da32873fdc..03993437ffcfd 100644 --- a/src/vs/base/test/common/extpath.test.ts +++ b/src/vs/base/test/common/extpath.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 * as extpath from 'vs/base/common/extpath'; import * as platform from 'vs/base/common/platform'; +import { CharCode } from 'vs/base/common/charCode'; suite('Paths', () => { @@ -28,7 +30,6 @@ suite('Paths', () => { assert.equal(extpath.getRoot('http://www/'), 'http://www/'); assert.equal(extpath.getRoot('file:///foo'), 'file:///'); assert.equal(extpath.getRoot('file://foo'), ''); - }); test('isUNC', () => { @@ -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 6f96e22f47e97..ffbe5c89c8789 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -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); @@ -498,4 +528,14 @@ suite('Filters', () => { fuzzyScore ); }); + + test('"Go to Symbol" with the exact method name doesn\'t work as expected #84787', function () { + const match = fuzzyScore(':get', ':get', 1, 'get', 'get', 0, true); + assert.ok(Boolean(match)); + }); + + test('Suggestion is not highlighted #85826', function () { + assertMatches('SemanticTokens', 'SemanticTokensEdits', '^S^e^m^a^n^t^i^c^T^o^k^e^n^sEdits', fuzzyScore); + assertMatches('SemanticTokens', 'SemanticTokensEdits', '^S^e^m^a^n^t^i^c^T^o^k^e^n^sEdits', fuzzyScoreGracefulAggressive); + }); }); diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts new file mode 100644 index 0000000000000..5aac1fb98b471 --- /dev/null +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -0,0 +1,1208 @@ +/*--------------------------------------------------------------------------------------------- + * 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 scorer from 'vs/base/common/fuzzyScorer'; +import { URI } from 'vs/base/common/uri'; +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 { + + getItemLabel(resource: URI): string { + return basename(resource.fsPath); + } + + getItemDescription(resource: URI): string { + return dirname(resource.fsPath); + } + + getItemPath(resource: URI): string { + return resource.fsPath; + } +} + +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 { + return undefined!; + } + + getItemDescription(resource: URI): string { + return undefined!; + } + + getItemPath(resource: URI): string { + return undefined!; + } +} + +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 _doScore2(target: string, query: string, matchOffset: number = 0): scorer.FuzzyScore2 { + const preparedQuery = scorer.prepareQuery(query); + + return scorer.scoreFuzzy2(target, preparedQuery, 0, matchOffset); +} + +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(); + +suite('Fuzzy Scorer', () => { + + test('score (fuzzy)', function () { + const target = 'HeLlo-World'; + + 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, 'W', true)); // direct 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 + + // Assert scoring order + let sortedScores = scores.concat().sort((a, b) => b[0] - a[0]); + assert.deepEqual(scores, sortedScores); + + // Assert scoring positions + // 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); + }); + + test('score (non fuzzy)', function () { + const target = 'HeLlo-World'; + + assert.ok(_doScore(target, 'HelLo-World', false)[0] > 0); + assert.equal(_doScore(target, 'HelLo-World', false)[1].length, 'HelLo-World'.length); + + assert.ok(_doScore(target, 'hello-world', false)[0] > 0); + assert.equal(_doScore(target, 'HW', false)[0], 0); + assert.ok(_doScore(target, 'h', false)[0] > 0); + assert.ok(_doScore(target, 'ello', false)[0] > 0); + assert.ok(_doScore(target, 'ld', false)[0] > 0); + assert.equal(_doScore(target, 'eo', false)[0], 0); + }); + + test('scoreItem - matches are proper', function () { + 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); + assert.ok(!res.score); + + // Path Identity + 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); + assert.equal(identityRes.descriptionMatch![0].start, 0); + assert.equal(identityRes.descriptionMatch![0].end, ResourceAccessor.getItemDescription(resource).length); + assert.equal(identityRes.labelMatch![0].start, 0); + assert.equal(identityRes.labelMatch![0].end, ResourceAccessor.getItemLabel(resource).length); + + // Basename Prefix + const basenamePrefixRes = scoreItem(resource, 'som', true, ResourceAccessor); + assert.ok(basenamePrefixRes.score); + assert.ok(!basenamePrefixRes.descriptionMatch); + assert.equal(basenamePrefixRes.labelMatch!.length, 1); + assert.equal(basenamePrefixRes.labelMatch![0].start, 0); + assert.equal(basenamePrefixRes.labelMatch![0].end, 'som'.length); + + // Basename Camelcase + const basenameCamelcaseRes = scoreItem(resource, 'sF', true, ResourceAccessor); + assert.ok(basenameCamelcaseRes.score); + assert.ok(!basenameCamelcaseRes.descriptionMatch); + assert.equal(basenameCamelcaseRes.labelMatch!.length, 2); + assert.equal(basenameCamelcaseRes.labelMatch![0].start, 0); + assert.equal(basenameCamelcaseRes.labelMatch![0].end, 1); + assert.equal(basenameCamelcaseRes.labelMatch![1].start, 4); + assert.equal(basenameCamelcaseRes.labelMatch![1].end, 5); + + // Basename Match + const basenameRes = scoreItem(resource, 'of', true, ResourceAccessor); + assert.ok(basenameRes.score); + assert.ok(!basenameRes.descriptionMatch); + assert.equal(basenameRes.labelMatch!.length, 2); + assert.equal(basenameRes.labelMatch![0].start, 1); + assert.equal(basenameRes.labelMatch![0].end, 2); + assert.equal(basenameRes.labelMatch![1].start, 4); + assert.equal(basenameRes.labelMatch![1].end, 5); + + // Path Match + const pathRes = scoreItem(resource, 'xyz123', true, ResourceAccessor); + assert.ok(pathRes.score); + assert.ok(pathRes.descriptionMatch); + assert.ok(pathRes.labelMatch); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 8); + assert.equal(pathRes.labelMatch![0].end, 11); + assert.equal(pathRes.descriptionMatch!.length, 1); + assert.equal(pathRes.descriptionMatch![0].start, 1); + assert.equal(pathRes.descriptionMatch![0].end, 4); + + // No Match + const noRes = scoreItem(resource, '987', true, ResourceAccessor); + assert.ok(!noRes.score); + assert.ok(!noRes.labelMatch); + assert.ok(!noRes.descriptionMatch); + + // Verify Scores + assert.ok(identityRes.score > basenamePrefixRes.score); + assert.ok(basenamePrefixRes.score > basenameRes.score); + assert.ok(basenameRes.score > pathRes.score); + 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); + assert.equal(res.score, 0); + + res = scoreItem(null, 'null', true, ResourceAccessor); + assert.equal(res.score, 0); + }); + + test('scoreItem - optimize for file paths', function () { + const resource = URI.file('/xyz/others/spath/some/xsp/file123.txt'); + + // 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); + assert.ok(pathRes.score); + assert.ok(pathRes.descriptionMatch); + assert.ok(pathRes.labelMatch); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 0); + assert.equal(pathRes.labelMatch![0].end, 7); + assert.equal(pathRes.descriptionMatch!.length, 1); + assert.equal(pathRes.descriptionMatch![0].start, 23); + assert.equal(pathRes.descriptionMatch![0].end, 26); + }); + + 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); + assert.ok(pathRes.score); + assert.ok(pathRes.descriptionMatch); + assert.ok(pathRes.labelMatch); + assert.equal(pathRes.labelMatch!.length, 1); + assert.equal(pathRes.labelMatch![0].start, 0); + assert.equal(pathRes.labelMatch![0].end, 9); + }); + + test('scoreItem - prefers more compact matches', function () { + const resource = URI.file('/1a111d1/11a1d1/something.txt'); + + // expect "ad" to be matched towards the end of the file because the + // match is more compact + const res = scoreItem(resource, 'ad', true, ResourceAccessor); + assert.ok(res.score); + assert.ok(res.descriptionMatch); + assert.ok(!res.labelMatch!.length); + assert.equal(res.descriptionMatch!.length, 2); + assert.equal(res.descriptionMatch![0].start, 11); + assert.equal(res.descriptionMatch![0].end, 12); + assert.equal(res.descriptionMatch![1].start, 13); + assert.equal(res.descriptionMatch![1].end, 14); + }); + + test('scoreItem - proper target offset', function () { + const resource = URI.file('etem'); + + 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); + + assert.equal(res.labelMatch!.length, 1); + assert.equal(res.labelMatch![0].start, 1); + assert.equal(res.labelMatch![0].end, 3); + }); + + 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); + + assert.equal(res.descriptionMatch!.length, 3); + assert.equal(res.descriptionMatch![0].start, 9); + assert.equal(res.descriptionMatch![0].end, 10); + assert.equal(res.descriptionMatch![1].start, 36); + assert.equal(res.descriptionMatch![1].end, 37); + assert.equal(res.descriptionMatch![2].start, 40); + assert.equal(res.descriptionMatch![2].end, 41); + + assert.equal(res.labelMatch!.length, 2); + assert.equal(res.labelMatch![0].start, 9); + assert.equal(res.labelMatch![0].end, 10); + assert.equal(res.labelMatch![1].start, 20); + assert.equal(res.labelMatch![1].end, 21); + }); + + test('scoreItem - no match unless query contained in sequence', function () { + const resource = URI.file('abcde'); + + 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'); + const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); + + // Full resource A path + let query = ResourceAccessor.getItemPath(resourceA); + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + assert.equal(res[2], resourceC); + + // Full resource B path + query = ResourceAccessor.getItemPath(resourceB); + + 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)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + assert.equal(res[2], resourceC); + }); + + test('compareFilesByScore - basename prefix', function () { + const resourceA = URI.file('/some/path/fileA.txt'); + const resourceB = URI.file('/some/path/other/fileB.txt'); + const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); + + // Full resource A basename + let query = ResourceAccessor.getItemLabel(resourceA); + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + assert.equal(res[2], resourceC); + + // Full resource B basename + query = ResourceAccessor.getItemLabel(resourceB); + + 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)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + assert.equal(res[2], resourceC); + }); + + test('compareFilesByScore - basename camelcase', function () { + const resourceA = URI.file('/some/path/fileA.txt'); + const resourceB = URI.file('/some/path/other/fileB.txt'); + const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); + + // resource A camelcase + let query = 'fA'; + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + assert.equal(res[2], resourceC); + + // resource B camelcase + query = 'fB'; + + 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)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + assert.equal(res[2], resourceC); + }); + + test('compareFilesByScore - basename scores', function () { + const resourceA = URI.file('/some/path/fileA.txt'); + const resourceB = URI.file('/some/path/other/fileB.txt'); + const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); + + // Resource A part of basename + let query = 'fileA'; + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + assert.equal(res[2], resourceC); + + // Resource B part of basename + query = 'fileB'; + + 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)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + assert.equal(res[2], resourceC); + }); + + test('compareFilesByScore - path scores', function () { + const resourceA = URI.file('/some/path/fileA.txt'); + const resourceB = URI.file('/some/path/other/fileB.txt'); + const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); + + // Resource A part of path + let query = 'pathfileA'; + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + assert.equal(res[2], resourceC); + + // Resource B part of path + query = 'pathfileB'; + + 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)); + assert.equal(res[0], resourceB); + assert.equal(res[1], resourceA); + assert.equal(res[2], resourceC); + }); + + test('compareFilesByScore - prefer shorter basenames', function () { + const resourceA = URI.file('/some/path/fileA.txt'); + const resourceB = URI.file('/some/path/other/fileBLonger.txt'); + const resourceC = URI.file('/unrelated/the/path/other/fileC.txt'); + + // Resource A part of path + let query = 'somepath'; + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + assert.equal(res[2], resourceC); + }); + + test('compareFilesByScore - prefer shorter basenames (match on basename)', function () { + const resourceA = URI.file('/some/path/fileA.txt'); + const resourceB = URI.file('/some/path/other/fileBLonger.txt'); + const resourceC = URI.file('/unrelated/the/path/other/fileC.txt'); + + // Resource A part of path + let query = 'file'; + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceC); + assert.equal(res[2], resourceB); + }); + + test('compareFilesByScore - prefer shorter paths', function () { + const resourceA = URI.file('/some/path/fileA.txt'); + const resourceB = URI.file('/some/path/other/fileB.txt'); + const resourceC = URI.file('/unrelated/some/path/other/fileC.txt'); + + // Resource A part of path + let query = 'somepath'; + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceB); + assert.equal(res[2], resourceC); + }); + + test('compareFilesByScore - prefer shorter paths (bug #17443)', function () { + const resourceA = URI.file('config/test/t1.js'); + const resourceB = URI.file('config/test.js'); + const resourceC = URI.file('config/test/t2.js'); + + let query = 'co/te'; + + 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 - 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 = 'partsquick'; + + 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)); + 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 - prefer more compact matches (label)', function () { + const resourceA = URI.file('config/test/examasdaple.js'); + const resourceB = URI.file('config/test/exampleasdaasd.ts'); + + let query = 'xp'; + + 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 - prefer more compact matches (path)', function () { + const resourceA = URI.file('config/test/examasdaple/file.js'); + const resourceB = URI.file('config/test/exampleasdaasd/file.ts'); + + let query = 'xp'; + + 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 - prefer more compact matches (label and path)', function () { + const resourceA = URI.file('config/example/thisfile.ts'); + const resourceB = URI.file('config/24234243244/example/file.js'); + + let query = 'exfile'; + + 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 - avoid match scattering (bug #34210)', function () { + const resourceA = URI.file('node_modules1/bundle/lib/model/modules/ot1/index.js'); + const resourceB = URI.file('node_modules1/bundle/lib/model/modules/un1/index.js'); + const resourceC = URI.file('node_modules1/bundle/lib/model/modules/modu1/index.js'); + const resourceD = URI.file('node_modules1/bundle/lib/model/modules/oddl1/index.js'); + + let query = isWindows ? 'modu1\\index.js' : 'modu1/index.js'; + + 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)); + 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)); + assert.equal(res[0], resourceB); + + res = [resourceC, resourceB, resourceA, resourceD].sort((r1, r2) => compareItemsByScore(r1, r2, query, true, ResourceAccessor)); + assert.equal(res[0], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #21019 1.)', function () { + const resourceA = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceLoad/index.js'); + const resourceB = URI.file('app/containers/Services/NetworkData/ServiceDetails/ServiceDistribution/index.js'); + const resourceC = URI.file('app/containers/Services/NetworkData/ServiceDetailTabs/ServiceTabs/StatVideo/index.js'); + + let query = 'StatVideoindex'; + + 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)); + assert.equal(res[0], resourceC); + }); + + test('compareFilesByScore - avoid match scattering (bug #21019 2.)', function () { + const resourceA = URI.file('src/build-helper/store/redux.ts'); + const resourceB = URI.file('src/repository/store/redux.ts'); + + let query = 'reproreduxts'; + + 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 - avoid match scattering (bug #26649)', function () { + const resourceA = URI.file('photobook/src/components/AddPagesButton/index.js'); + const resourceB = URI.file('photobook/src/components/ApprovalPageHeader/index.js'); + const resourceC = URI.file('photobook/src/canvasComponents/BookPage/index.js'); + + let query = 'bookpageIndex'; + + 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)); + assert.equal(res[0], resourceC); + }); + + test('compareFilesByScore - avoid match scattering (bug #33247)', function () { + const resourceA = URI.file('ui/src/utils/constants.js'); + const resourceB = URI.file('ui/src/ui/Icons/index.js'); + + let query = isWindows ? 'ui\\icons' : 'ui/icons'; + + 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 - avoid match scattering (bug #33247 comment)', function () { + const resourceA = URI.file('ui/src/components/IDInput/index.js'); + const resourceB = URI.file('ui/src/ui/Input/index.js'); + + let query = isWindows ? 'ui\\input\\index' : 'ui/input/index'; + + 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 - avoid match scattering (bug #36166)', function () { + const resourceA = URI.file('django/contrib/sites/locale/ga/LC_MESSAGES/django.mo'); + const resourceB = URI.file('django/core/signals.py'); + + let query = 'djancosig'; + + 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 - avoid match scattering (bug #32918)', function () { + const resourceA = URI.file('adsys/protected/config.php'); + const resourceB = URI.file('adsys/protected/framework/smarty/sysplugins/smarty_internal_config.php'); + const resourceC = URI.file('duowanVideo/wap/protected/config.php'); + + let query = 'protectedconfig.php'; + + 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)); + assert.equal(res[0], resourceA); + assert.equal(res[1], resourceC); + assert.equal(res[2], resourceB); + }); + + test('compareFilesByScore - avoid match scattering (bug #14879)', function () { + const resourceA = URI.file('pkg/search/gradient/testdata/constraint_attrMatchString.yml'); + const resourceB = URI.file('cmd/gradient/main.go'); + + let query = 'gradientmain'; + + 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 - avoid match scattering (bug #14727 1)', function () { + const resourceA = URI.file('alpha-beta-cappa.txt'); + const resourceB = URI.file('abc.txt'); + + let query = 'abc'; + + 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 - avoid match scattering (bug #14727 2)', function () { + const resourceA = URI.file('xerxes-yak-zubba/index.js'); + const resourceB = URI.file('xyz/index.js'); + + let query = 'xyz'; + + 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 - avoid match scattering (bug #18381)', function () { + const resourceA = URI.file('AssymblyInfo.cs'); + const resourceB = URI.file('IAsynchronousTask.java'); + + let query = 'async'; + + 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 - avoid match scattering (bug #35572)', function () { + const resourceA = URI.file('static/app/source/angluar/-admin/-organization/-settings/layout/layout.js'); + const resourceB = URI.file('static/app/source/angular/-admin/-project/-settings/_settings/settings.js'); + + let query = 'partisettings'; + + 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 - avoid match scattering (bug #36810)', function () { + const resourceA = URI.file('Trilby.TrilbyTV.Web.Portal/Views/Systems/Index.cshtml'); + const resourceB = URI.file('Trilby.TrilbyTV.Web.Portal/Areas/Admins/Views/Tips/Index.cshtml'); + + let query = 'tipsindex.cshtml'; + + 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 hit (bug #20546)', function () { + const resourceA = URI.file('editor/core/components/tests/list-view-spec.js'); + const resourceB = URI.file('editor/core/components/list-view.js'); + + let query = 'listview'; + + 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 - avoid match scattering (bug #12095)', function () { + const resourceA = URI.file('src/vs/workbench/contrib/files/common/explorerViewModel.ts'); + const resourceB = URI.file('src/vs/workbench/contrib/files/browser/views/explorerView.ts'); + const resourceC = URI.file('src/vs/workbench/contrib/files/browser/views/explorerViewer.ts'); + + let query = 'filesexplorerview.ts'; + + 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)); + assert.equal(res[0], resourceB); + }); + + 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 d197cdf4f1f99..b5074f4ffa5c4 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,15 +32,67 @@ 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' })); assert.notEqual(hash({}), hash([])); }); -}); \ No newline at end of file + + test('array - unexpected collision', function () { + const a = hash([undefined, undefined, undefined, undefined, undefined]); + const b = hash([undefined, undefined, 'HHHHHH', [{ line: 0, character: 0 }, { line: 0, character: 0 }], undefined]); + 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 fc32e74324be0..8b92348be6f34 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 b7a165c509503..7f32bc3eb6abf 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/jsonEdit.test.ts b/src/vs/base/test/common/jsonEdit.test.ts index 194f246a402f9..09936392c2436 100644 --- a/src/vs/base/test/common/jsonEdit.test.ts +++ b/src/vs/base/test/common/jsonEdit.test.ts @@ -118,13 +118,43 @@ suite('JSON - edits', () => { assertEdit(content, edits, '{\n "x": "y"\n}'); }); - test('insert item to empty array', () => { + test('insert item at 0', () => { + let content = '[\n 2,\n 3\n]'; + let edits = setProperty(content, [0], 1, formatterOptions); + assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]'); + }); + + test('insert item at 0 in empty array', () => { + let content = '[\n]'; + let edits = setProperty(content, [0], 1, formatterOptions); + assertEdit(content, edits, '[\n 1\n]'); + }); + + test('insert item at an index', () => { + let content = '[\n 1,\n 3\n]'; + let edits = setProperty(content, [1], 2, formatterOptions); + assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]'); + }); + + test('insert item at an index im empty array', () => { + let content = '[\n]'; + let edits = setProperty(content, [1], 1, formatterOptions); + assertEdit(content, edits, '[\n 1\n]'); + }); + + test('insert item at end index', () => { + let content = '[\n 1,\n 2\n]'; + let edits = setProperty(content, [2], 3, formatterOptions); + assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]'); + }); + + test('insert item at end to empty array', () => { let content = '[\n]'; let edits = setProperty(content, [-1], 'bar', formatterOptions); assertEdit(content, edits, '[\n "bar"\n]'); }); - test('insert item', () => { + test('insert item at end', () => { let content = '[\n 1,\n 2\n]'; let edits = setProperty(content, [-1], 'bar', formatterOptions); assertEdit(content, edits, '[\n 1,\n 2,\n "bar"\n]'); @@ -160,4 +190,4 @@ suite('JSON - edits', () => { assertEdit(content, edits, '// This is a comment\n[\n 1,\n "foo"\n]'); }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/common/lazy.test.ts b/src/vs/base/test/common/lazy.test.ts index c6a1655513fbe..04d4a2569858b 100644 --- a/src/vs/base/test/common/lazy.test.ts +++ b/src/vs/base/test/common/lazy.test.ts @@ -47,7 +47,7 @@ suite('Lazy', () => { assert.deepEqual(innerLazy.getValue(), [1, 11]); }); - test('map should should handle error values', () => { + test('map should handle error values', () => { let outer = 0; let inner = 10; const outerLazy = new Lazy(() => { throw new Error(`${++outer}`); }); diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index 4d15ad2046c4c..7aa87cc6b0fec 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 7dc178dbbc083..e63fda850acae 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 0000000000000..a7b61a558c2b4 --- /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 245cccdf776b1..47af3ca64a95b 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,24 +14,26 @@ 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'); }); test('LinkedMap - Touch Old one', () => { 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', () => { @@ -39,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', () => { @@ -48,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', () => { @@ -58,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', () => { @@ -68,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 () { @@ -128,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); @@ -135,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]); @@ -149,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]); @@ -168,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++) { @@ -180,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', () => { @@ -192,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', () => { @@ -221,7 +278,6 @@ suite('Map', () => { assert.equal(key, 'ck'); assert.equal(value, 'cv'); } - i++; }); }); @@ -236,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 () { @@ -250,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 () { @@ -265,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); }); @@ -311,7 +367,66 @@ suite('Map', () => { assert.equal(iter.hasNext(), false); }); - function assertTernarySearchTree(trie: TernarySearchTree, ...elements: [string, E][]) { + test('URIIterator', function () { + const iter = new UriIterator(); + 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); @@ -377,7 +492,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); @@ -407,7 +522,7 @@ suite('Map', () => { }); test('TernarySearchTree - delete & cleanup', function () { - let trie = new TernarySearchTree(new StringIterator()); + let trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('foobar', 2); trie.set('bar', 3); @@ -417,7 +532,7 @@ suite('Map', () => { }); 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); @@ -441,7 +556,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); @@ -455,7 +570,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); @@ -492,6 +607,100 @@ suite('Map', () => { assert.equal(map.findSuperstr('/userr'), undefined); }); + + test('TernarySearchTree (URI) - basics', function () { + let trie = new TernarySearchTree(new UriIterator()); + + 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()); + 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()); + 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(); @@ -504,18 +713,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); @@ -600,45 +812,32 @@ suite('Map', () => { assert.equal(map.get(uncFile), 'true'); }); - // test('ResourceMap - files (ignorecase)', function () { - // const map = new ResourceMap(true); - - // 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'); + test('ResourceMap - files (ignorecase)', function () { + const map = new ResourceMap(uri => extUriIgnorePathCase.getComparisonKey(uri)); - // assert.equal(map.get(fileAUpper), 'true'); + const fileA = URI.parse('file://some/filea'); + const fileB = URI.parse('some://some/other/fileb'); + const fileAUpper = URI.parse('file://SOME/FILEA'); - // assert.ok(!map.get(fileB)); + map.set(fileA, 'true'); + assert.equal(map.get(fileA), 'true'); - // map.set(fileAUpper, 'false'); - // assert.equal(map.get(fileAUpper), 'false'); + assert.equal(map.get(fileAUpper), 'true'); - // assert.equal(map.get(fileA), 'false'); + assert.ok(!map.get(fileB)); - // const windowsFile = URI.file('c:\\test with %25\\c#code'); - // const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json'); + map.set(fileAUpper, 'false'); + assert.equal(map.get(fileAUpper), 'false'); - // map.set(windowsFile, 'true'); - // map.set(uncFile, 'true'); + assert.equal(map.get(fileA), 'false'); - // assert.equal(map.get(windowsFile), 'true'); - // assert.equal(map.get(uncFile), 'true'); - // }); + const windowsFile = URI.file('c:\\test with %25\\c#code'); + const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json'); - test('mapToSerializable / serializableToMap', function () { - const map = new Map(); - map.set('1', 'foo'); - map.set('2', null!); - map.set('3', 'bar'); + map.set(windowsFile, 'true'); + map.set(uncFile, 'true'); - 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 69d33de8f1709..f33f741f624ad 100644 --- a/src/vs/base/test/common/markdownString.test.ts +++ b/src/vs/base/test/common/markdownString.test.ts @@ -6,14 +6,67 @@ import * as assert from 'assert'; import { MarkdownString } from 'vs/base/common/htmlContent'; -suite('markdownString', () => { +suite('MarkdownString', () => { - test('escape', () => { + test('appendText', () => { const mds = new MarkdownString(); - mds.appendText('# foo\n*bar*'); assert.equal(mds.value, '\\# foo\n\n\\*bar\\*'); }); + + suite('ThemeIcons', () => { + + suite('Support On', () => { + + test('appendText', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendText('$(zap) $(not a theme icon) $(add)'); + + assert.equal(mds.value, '\\\\$\\(zap\\) $\\(not a theme icon\\) \\\\$\\(add\\)'); + }); + + test('appendMarkdown', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown('$(zap) $(not a theme icon) $(add)'); + + assert.equal(mds.value, '$(zap) $(not a theme icon) $(add)'); + }); + + test('appendMarkdown with escaped icon', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)'); + + assert.equal(mds.value, '\\$(zap) $(not a theme icon) $(add)'); + }); + + }); + + suite('Support Off', () => { + + test('appendText', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: false }); + mds.appendText('$(zap) $(not a theme icon) $(add)'); + + assert.equal(mds.value, '$\\(zap\\) $\\(not a theme icon\\) $\\(add\\)'); + }); + + test('appendMarkdown', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: false }); + mds.appendMarkdown('$(zap) $(not a theme icon) $(add)'); + + assert.equal(mds.value, '$(zap) $(not a theme icon) $(add)'); + }); + + test('appendMarkdown with escaped icon', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)'); + + assert.equal(mds.value, '\\$(zap) $(not a theme icon) $(add)'); + }); + + }); + + }); }); diff --git a/src/vs/base/test/common/mime.test.ts b/src/vs/base/test/common/mime.test.ts index 7c99c964c75b0..a7ac5a177a6a5 100644 --- a/src/vs/base/test/common/mime.test.ts +++ b/src/vs/base/test/common/mime.test.ts @@ -2,8 +2,9 @@ * 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 { 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', () => { @@ -125,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 0000000000000..aebe84a0c6e88 --- /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/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 5fdf1ba108899..1e0d4496e26cb 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.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 { 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'; @@ -66,6 +66,8 @@ suite('Resources', () => { // 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 +110,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 +158,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 +235,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 +257,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); @@ -342,50 +349,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); - - assert.equal(isEqual(fileURI, fileURI3, true), false); - - assert.equal(isEqual(URI.parse('foo://server'), URI.parse('foo://server/')), true); + 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); + + assertIsEqual(fileURI, fileURI3, true, false); + + 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 0000000000000..f6a083e3cb2fe --- /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 6d86fbc2eb284..0ef3b7730e95c 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 ca5c6d3483e01..bce692cd6c9a5 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'); @@ -404,61 +416,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); @@ -492,4 +449,8 @@ suite('Strings', () => { assertEncodeDecodeUTF8('🧝', [240, 159, 167, 157]); }); + + test('getGraphemeBreakType', () => { + assert.equal(strings.getGraphemeBreakType(0xBC1), strings.GraphemeBreakType.SpacingMark); + }); }); diff --git a/src/vs/base/test/common/types.test.ts b/src/vs/base/test/common/types.test.ts index 90e174dd0009f..0bec27fcd846e 100644 --- a/src/vs/base/test/common/types.test.ts +++ b/src/vs/base/test/common/types.test.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 assert from 'assert'; import * as types from 'vs/base/common/types'; suite('Types', () => { + test('isFunction', () => { assert(!types.isFunction(undefined)); assert(!types.isFunction(null)); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 3255575ae5717..bc4e409c02d44 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'); @@ -439,6 +439,10 @@ suite('URI', () => { assert.equal(uri.path, uri2.path); }); + test('Unable to open \'%A0.txt\': URI malformed #76506', function () { + assert.equal(URI.parse('file://some/%.txt'), 'file://some/%25.txt'); + assert.equal(URI.parse('file://some/%A0.txt'), 'file://some/%25A0.txt'); + }); test('Links in markdown are broken if url contains encoded parameters #79474', function () { this.skip(); @@ -499,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 26b225a1fa900..9175dad6365dd 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 { diff --git a/src/vs/base/test/common/uuid.test.ts b/src/vs/base/test/common/uuid.test.ts index ef2cd78be28cc..ce07ab9cb19a3 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 69dfb89e8a133..4c7d0ba3a2ffc 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 3fa8f78de6828..0000000000000 --- 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/encoding/encoding.test.ts b/src/vs/base/test/node/encoding/encoding.test.ts deleted file mode 100644 index 77ad22264cf6e..0000000000000 --- a/src/vs/base/test/node/encoding/encoding.test.ts +++ /dev/null @@ -1,316 +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 fs from 'fs'; -import * as encoding from 'vs/base/node/encoding'; -import { Readable } from 'stream'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; - -export async function detectEncodingByBOM(file: string): Promise { - try { - const { buffer, bytesRead } = await readExactlyByFile(file, 3); - - return encoding.detectEncodingByBOMFromBuffer(buffer, bytesRead); - } catch (error) { - return null; // ignore errors (like file not found) - } -} - -interface ReadResult { - buffer: Buffer | null; - bytesRead: number; -} - -function readExactlyByFile(file: string, totalBytes: number): Promise { - return new Promise((resolve, reject) => { - fs.open(file, 'r', null, (err, fd) => { - if (err) { - return reject(err); - } - - function end(err: Error | null, resultBuffer: Buffer | null, bytesRead: number): 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({ buffer: resultBuffer, bytesRead }); - }); - } - - const buffer = Buffer.allocUnsafe(totalBytes); - let offset = 0; - - function readChunk(): void { - fs.read(fd, buffer, offset, totalBytes - offset, null, (err, bytesRead) => { - if (err) { - return end(err, null, 0); - } - - if (bytesRead === 0) { - return end(null, buffer, offset); - } - - offset += bytesRead; - - if (offset === totalBytes) { - return end(null, buffer, offset); - } - - return readChunk(); - }); - } - - readChunk(); - }); - }); -} - -suite('Encoding', () => { - - test('detectBOM does not return error for non existing file', async () => { - const file = getPathFromAmdModule(require, './fixtures/not-exist.css'); - - const detectedEncoding = await detectEncodingByBOM(file); - assert.equal(detectedEncoding, null); - }); - - test('detectBOM UTF-8', async () => { - const file = getPathFromAmdModule(require, './fixtures/some_utf8.css'); - - const detectedEncoding = await detectEncodingByBOM(file); - assert.equal(detectedEncoding, 'utf8'); - }); - - test('detectBOM UTF-16 LE', async () => { - const file = getPathFromAmdModule(require, './fixtures/some_utf16le.css'); - - const detectedEncoding = await detectEncodingByBOM(file); - assert.equal(detectedEncoding, 'utf16le'); - }); - - test('detectBOM UTF-16 BE', async () => { - const file = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); - - const detectedEncoding = await detectEncodingByBOM(file); - assert.equal(detectedEncoding, 'utf16be'); - }); - - test('detectBOM ANSI', async function () { - const file = getPathFromAmdModule(require, './fixtures/some_ansi.css'); - - const detectedEncoding = await detectEncodingByBOM(file); - assert.equal(detectedEncoding, null); - }); - - test('detectBOM ANSI', async function () { - const file = getPathFromAmdModule(require, './fixtures/empty.txt'); - - const detectedEncoding = await detectEncodingByBOM(file); - assert.equal(detectedEncoding, null); - }); - - test('resolve terminal encoding (detect)', async function () { - const enc = await encoding.resolveTerminalEncoding(); - assert.ok(encoding.encodingExists(enc)); - }); - - test('resolve terminal encoding (environment)', async function () { - process.env['VSCODE_CLI_ENCODING'] = 'utf16le'; - - const enc = await encoding.resolveTerminalEncoding(); - assert.ok(encoding.encodingExists(enc)); - assert.equal(enc, 'utf16le'); - }); - - test('detectEncodingFromBuffer (JSON saved as PNG)', async function () { - const file = getPathFromAmdModule(require, './fixtures/some.json.png'); - - const buffer = await readExactlyByFile(file, 512); - const mimes = encoding.detectEncodingFromBuffer(buffer); - assert.equal(mimes.seemsBinary, false); - }); - - test('detectEncodingFromBuffer (PNG saved as TXT)', async function () { - const file = getPathFromAmdModule(require, './fixtures/some.png.txt'); - const buffer = await readExactlyByFile(file, 512); - const mimes = encoding.detectEncodingFromBuffer(buffer); - assert.equal(mimes.seemsBinary, true); - }); - - test('detectEncodingFromBuffer (XML saved as PNG)', async function () { - const file = getPathFromAmdModule(require, './fixtures/some.xml.png'); - const buffer = await readExactlyByFile(file, 512); - const mimes = encoding.detectEncodingFromBuffer(buffer); - assert.equal(mimes.seemsBinary, false); - }); - - test('detectEncodingFromBuffer (QWOFF saved as TXT)', async function () { - const file = getPathFromAmdModule(require, './fixtures/some.qwoff.txt'); - const buffer = await readExactlyByFile(file, 512); - const mimes = encoding.detectEncodingFromBuffer(buffer); - assert.equal(mimes.seemsBinary, true); - }); - - test('detectEncodingFromBuffer (CSS saved as QWOFF)', async function () { - const file = getPathFromAmdModule(require, './fixtures/some.css.qwoff'); - const buffer = await readExactlyByFile(file, 512); - const mimes = encoding.detectEncodingFromBuffer(buffer); - assert.equal(mimes.seemsBinary, false); - }); - - test('detectEncodingFromBuffer (PDF)', async function () { - const file = getPathFromAmdModule(require, './fixtures/some.pdf'); - const buffer = await readExactlyByFile(file, 512); - const mimes = encoding.detectEncodingFromBuffer(buffer); - assert.equal(mimes.seemsBinary, true); - }); - - test('detectEncodingFromBuffer (guess UTF-16 LE from content without BOM)', async function () { - const file = getPathFromAmdModule(require, './fixtures/utf16_le_nobom.txt'); - const buffer = await readExactlyByFile(file, 512); - const mimes = encoding.detectEncodingFromBuffer(buffer); - assert.equal(mimes.encoding, encoding.UTF16le); - assert.equal(mimes.seemsBinary, false); - }); - - test('detectEncodingFromBuffer (guess UTF-16 BE from content without BOM)', async function () { - const file = getPathFromAmdModule(require, './fixtures/utf16_be_nobom.txt'); - const buffer = await readExactlyByFile(file, 512); - const mimes = encoding.detectEncodingFromBuffer(buffer); - assert.equal(mimes.encoding, encoding.UTF16be); - assert.equal(mimes.seemsBinary, false); - }); - - test('autoGuessEncoding (ShiftJIS)', async function () { - const file = getPathFromAmdModule(require, './fixtures/some.shiftjis.txt'); - const buffer = await readExactlyByFile(file, 512 * 8); - const mimes = await encoding.detectEncodingFromBuffer(buffer, true); - assert.equal(mimes.encoding, 'shiftjis'); - }); - - test('autoGuessEncoding (CP1252)', async function () { - const file = getPathFromAmdModule(require, './fixtures/some.cp1252.txt'); - const buffer = await readExactlyByFile(file, 512 * 8); - const mimes = await encoding.detectEncodingFromBuffer(buffer, true); - assert.equal(mimes.encoding, 'windows1252'); - }); - - async function readAndDecodeFromDisk(path: string, fileEncoding: string | null) { - return new Promise((resolve, reject) => { - fs.readFile(path, (err, data) => { - if (err) { - reject(err); - } else { - resolve(encoding.decode(data, fileEncoding!)); - } - }); - }); - } - - async function readAllAsString(stream: NodeJS.ReadableStream) { - return new Promise((resolve, reject) => { - let all = ''; - stream.on('data', chunk => { - all += chunk; - assert.equal(typeof chunk, 'string'); - }); - stream.on('end', () => { - resolve(all); - }); - stream.on('error', reject); - }); - } - - test('toDecodeStream - some stream', async function () { - - let source = new Readable({ - read(size) { - this.push(Buffer.from([65, 66, 67])); - this.push(Buffer.from([65, 66, 67])); - this.push(Buffer.from([65, 66, 67])); - this.push(null); - } - }); - - let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); - - assert.ok(detected); - assert.ok(stream); - - const content = await readAllAsString(stream); - assert.equal(content, 'ABCABCABC'); - }); - - test('toDecodeStream - some stream, expect too much data', async function () { - - let source = new Readable({ - read(size) { - this.push(Buffer.from([65, 66, 67])); - this.push(Buffer.from([65, 66, 67])); - this.push(Buffer.from([65, 66, 67])); - this.push(null); - } - }); - - let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); - - assert.ok(detected); - assert.ok(stream); - - const content = await readAllAsString(stream); - assert.equal(content, 'ABCABCABC'); - }); - - test('toDecodeStream - some stream, no data', async function () { - - let source = new Readable({ - read(size) { - this.push(null); // empty - } - }); - - let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 512, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); - - assert.ok(detected); - assert.ok(stream); - - const content = await readAllAsString(stream); - assert.equal(content, ''); - }); - - - test('toDecodeStream - encoding, utf16be', async function () { - - let path = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); - let source = fs.createReadStream(path); - - let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); - - assert.equal(detected.encoding, 'utf16be'); - assert.equal(detected.seemsBinary, false); - - let expected = await readAndDecodeFromDisk(path, detected.encoding); - let actual = await readAllAsString(stream); - assert.equal(actual, expected); - }); - - - test('toDecodeStream - empty file', async function () { - - let path = getPathFromAmdModule(require, './fixtures/empty.txt'); - let source = fs.createReadStream(path); - let { detected, stream } = await encoding.toDecodeStream(source, { guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 }); - - let expected = await readAndDecodeFromDisk(path, detected.encoding); - let actual = await readAllAsString(stream); - assert.equal(actual, expected); - }); -}); diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index f2610a7ee5f43..2c0288e389710 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -14,12 +14,12 @@ suite('Glob', () => { // let patterns = [ // '{**/*.cs,**/*.json,**/*.csproj,**/*.sln}', // '{**/*.cs,**/*.csproj,**/*.sln}', - // '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs}', + // '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs}', // '**/*.go', // '{**/*.ps,**/*.ps1}', // '{**/*.c,**/*.cpp,**/*.h}', // '{**/*.fsx,**/*.fsi,**/*.fs,**/*.ml,**/*.mli}', - // '{**/*.js,**/*.jsx,**/*.es6,**/*.mjs}', + // '{**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs}', // '{**/*.ts,**/*.tsx}', // '{**/*.php}', // '{**/*.php}', @@ -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/**'; @@ -1015,4 +1012,4 @@ suite('Glob', () => { assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); } }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/node/keytar.test.ts b/src/vs/base/test/node/keytar.test.ts index 53f1c31a1a64d..b2f7bab5042f4 100644 --- a/src/vs/base/test/node/keytar.test.ts +++ b/src/vs/base/test/node/keytar.test.ts @@ -28,7 +28,7 @@ suite('Keytar', () => { try { await keytar.deletePassword(name, 'foo'); } finally { - // tslint:disable-next-line: no-unsafe-finally + // eslint-disable-next-line no-unsafe-finally throw err; } } 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 f5230c4a35c73..3150f8d60942f 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 aad4add0e72be..fd324076b2670 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -7,49 +7,21 @@ 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'; -import { join } from 'path'; -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[] = []; +suite('PFS', function () { - 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', () => { + // Given issues such as https://github.com/microsoft/vscode/issues/84066 + // we see random test failures when accessing the native file system. To + // diagnose further, we retry node.js file access tests up to 3 times to + // rule out any random disk issue. + this.retries(3); test('writeFile', async () => { const id = uuid.generateUuid(); @@ -252,7 +224,6 @@ suite('PFS', () => { } catch (error) { assert.fail(error); - throw error; } }); @@ -329,7 +300,7 @@ suite('PFS', () => { 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(); @@ -344,12 +315,36 @@ suite('PFS', () => { 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 () => { @@ -373,12 +368,12 @@ suite('PFS', () => { if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const testDir = join(parentDir, 'pfs', id); + const testDir = path.join(parentDir, 'pfs', id); const newDir = path.join(testDir, 'öäü'); await pfs.mkdirp(newDir, 493); - await pfs.writeFile(join(testDir, 'somefile.txt'), 'contents'); + await pfs.writeFile(path.join(testDir, 'somefile.txt'), 'contents'); assert.ok(fs.existsSync(newDir)); @@ -415,17 +410,10 @@ suite('PFS', () => { 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(); @@ -445,22 +433,6 @@ suite('PFS', () => { 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); @@ -485,118 +457,6 @@ suite('PFS', () => { 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/node/test/fixtures/extract.zip b/src/vs/base/test/node/zip/fixtures/extract.zip similarity index 100% rename from src/vs/base/node/test/fixtures/extract.zip rename to src/vs/base/test/node/zip/fixtures/extract.zip diff --git a/src/vs/base/node/test/zip.test.ts b/src/vs/base/test/node/zip/zip.test.ts similarity index 100% rename from src/vs/base/node/test/zip.test.ts rename to src/vs/base/test/node/zip/zip.test.ts diff --git a/src/vs/base/worker/defaultWorkerFactory.ts b/src/vs/base/worker/defaultWorkerFactory.ts index 45f3b41a45735..1f2d5dbb79eb3 100644 --- a/src/vs/base/worker/defaultWorkerFactory.ts +++ b/src/vs/base/worker/defaultWorkerFactory.ts @@ -28,11 +28,11 @@ 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'; diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 22bc88f1c00ba..71c6724e984e7 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 da6c907b666ab..9aec539bb55cd 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 7ae960dbc913a..0142bb7f53dfa 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,22 +30,28 @@ + diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index dbcb8e490fefa..d56e3fdd36a7c 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -2,6 +2,10 @@ + @@ -10,9 +14,6 @@ - - - @@ -27,20 +28,27 @@ + diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index a599f5a7ebf07..9b814729d318e 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 } 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,8 @@ 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'; interface ICredential { service: string; @@ -116,8 +120,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(); @@ -273,6 +277,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 +379,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,11 +399,60 @@ 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}`; + }; + + // Credentials (with support of predefined ones via meta element) + const credentialsProvider = new LocalStorageCredentialsProvider(); + + const credentialsElement = document.getElementById('vscode-workbench-credentials'); + const credentialsElementAttribute = credentialsElement ? credentialsElement.getAttribute('data-settings') : undefined; + let credentials = undefined; + if (credentialsElementAttribute) { + try { + credentials = JSON.parse(credentialsElementAttribute); + for (const { service, account, password } of credentials) { + credentialsProvider.setPassword(service, account, password); + } + } catch (error) { /* Invalid credentials are passed. Ignore. */ } + } + // Finally create workbench create(document.body, { ...config, - workspaceProvider: new WorkspaceProvider(workspace, payload), + homeIndicator, + windowIndicator, + productQualityChangeHandler, + workspaceProvider, urlCallbackProvider: new PollingURLCallbackProvider(), - credentialsProvider: new LocalStorageCredentialsProvider() + credentialsProvider }); })(); diff --git a/src/vs/code/buildfile.js b/src/vs/code/buildfile.js index 221bea2144396..9b1ee16680e43 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 695de78a4cb9e..0000000000000 --- a/src/vs/code/electron-browser/issue/issueReporter.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/vs/code/electron-browser/issue/issueReporter.js b/src/vs/code/electron-browser/issue/issueReporter.js deleted file mode 100644 index 5d11dd15ae463..0000000000000 --- a/src/vs/code/electron-browser/issue/issueReporter.js +++ /dev/null @@ -1,13 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 bootstrapWindow = require('../../../../bootstrap-window'); - -bootstrapWindow.load(['vs/code/electron-browser/issue/issueReporterMain'], function (issueReporter, configuration) { - issueReporter.startup(configuration); -}, { forceEnableDeveloperKeybindings: true, disallowReloadKeybinding: true }); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts deleted file mode 100644 index c553e51c8e011..0000000000000 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ /dev/null @@ -1,1155 +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/issueReporter'; -import { shell, ipcRenderer, webFrame, clipboard } from 'electron'; -import { localize } from 'vs/nls'; -import { $ } from 'vs/base/browser/dom'; -import * as collections from 'vs/base/common/collections'; -import * as browser from 'vs/base/browser/browser'; -import { escape } from 'vs/base/common/strings'; -import product from 'vs/platform/product/common/product'; -import * as os from 'os'; -import { debounce } from 'vs/base/common/decorators'; -import * as platform from 'vs/base/common/platform'; -import { Disposable } from 'vs/base/common/lifecycle'; -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 { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { MainProcessService, IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel'; -import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; -import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; -import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; -import { CodiconLabel } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; -import { normalizeGitHubUrl } from 'vs/code/common/issue/issueReporterUtil'; -import { Button } from 'vs/base/browser/ui/button/button'; -import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; -import { SpdLogService } from 'vs/platform/log/node/spdlogService'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; - -const MAX_URL_LENGTH = 2045; - -interface SearchResult { - html_url: string; - title: string; - state?: string; -} - -export interface IssueReporterConfiguration extends IWindowConfiguration { - data: IssueReporterData; - features: IssueReporterFeatures; -} - -export function startup(configuration: IssueReporterConfiguration) { - document.body.innerHTML = BaseHtml(); - const issueReporter = new IssueReporter(configuration); - issueReporter.render(); - document.body.style.display = 'block'; - issueReporter.setInitialFocus(); -} - -export class IssueReporter extends Disposable { - private environmentService!: IEnvironmentService; - private telemetryService!: ITelemetryService; - private logService!: ILogService; - private readonly issueReporterModel: IssueReporterModel; - private numberOfSearchResultsDisplayed = 0; - private receivedSystemInfo = false; - private receivedPerformanceInfo = false; - private shouldQueueSearch = false; - private hasBeenSubmitted = false; - - private readonly previewButton!: Button; - - constructor(configuration: IssueReporterConfiguration) { - super(); - - this.initServices(configuration); - - const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION; - 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' : ''}` - }, - 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 - }); - - const issueReporterElement = this.getElementById('issue-reporter'); - if (issueReporterElement) { - this.previewButton = new Button(issueReporterElement); - } - - ipcRenderer.on('vscode:issuePerformanceInfoResponse', (_: unknown, info: Partial) => { - this.logService.trace('issueReporter: Received performance data'); - this.issueReporterModel.update(info); - this.receivedPerformanceInfo = true; - - const state = this.issueReporterModel.getData(); - this.updateProcessInfo(state); - this.updateWorkspaceInfo(state); - this.updatePreviewButtonState(); - }); - - ipcRenderer.on('vscode:issueSystemInfoResponse', (_: unknown, info: SystemInfo) => { - this.logService.trace('issueReporter: Received system data'); - this.issueReporterModel.update({ systemInfo: info }); - this.receivedSystemInfo = true; - - this.updateSystemInfo(this.issueReporterModel.getData()); - this.updatePreviewButtonState(); - }); - - ipcRenderer.send('vscode:issueSystemInfoRequest'); - 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')); - } - - this.setUpTypes(); - this.setEventHandlers(); - this.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 { - this.renderBlocks(); - } - - setInitialFocus() { - const { fileOnExtension } = this.issueReporterModel.getData(); - if (fileOnExtension) { - const issueTitle = document.getElementById('issue-title'); - if (issueTitle) { - issueTitle.focus(); - } - } else { - const issueType = document.getElementById('issue-type'); - if (issueType) { - issueType.focus(); - } - } - } - - 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[] = []; - - if (styles.inputBackground) { - content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { background-color: ${styles.inputBackground}; }`); - } - - if (styles.inputBorder) { - content.push(`input[type="text"], textarea, select { border: 1px solid ${styles.inputBorder}; }`); - } else { - content.push(`input[type="text"], textarea, select { border: 1px solid transparent; }`); - } - - if (styles.inputForeground) { - content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { color: ${styles.inputForeground}; }`); - } - - if (styles.inputErrorBorder) { - content.push(`.invalid-input, .invalid-input:focus { border: 1px solid ${styles.inputErrorBorder} !important; }`); - content.push(`.validation-error, .required-input { color: ${styles.inputErrorBorder}; }`); - } - - if (styles.inputActiveBorder) { - content.push(`input[type='text']:focus, textarea:focus, select:focus, summary:focus, button:focus, a:focus, .workbenchCommand:focus { border: 1px solid ${styles.inputActiveBorder}; outline-style: none; }`); - } - - if (styles.textLinkColor) { - content.push(`a, .workbenchCommand { color: ${styles.textLinkColor}; }`); - } - - if (styles.textLinkColor) { - content.push(`a { color: ${styles.textLinkColor}; }`); - } - - if (styles.textLinkActiveForeground) { - content.push(`a:hover, .workbenchCommand:hover { color: ${styles.textLinkActiveForeground}; }`); - } - - if (styles.sliderBackgroundColor) { - content.push(`::-webkit-scrollbar-thumb { background-color: ${styles.sliderBackgroundColor}; }`); - } - - if (styles.sliderActiveColor) { - content.push(`::-webkit-scrollbar-thumb:active { background-color: ${styles.sliderActiveColor}; }`); - } - - if (styles.sliderHoverColor) { - content.push(`::--webkit-scrollbar-thumb:hover { background-color: ${styles.sliderHoverColor}; }`); - } - - if (styles.buttonBackground) { - content.push(`.monaco-text-button { background-color: ${styles.buttonBackground} !important; }`); - } - - if (styles.buttonForeground) { - content.push(`.monaco-text-button { color: ${styles.buttonForeground} !important; }`); - } - - if (styles.buttonHoverBackground) { - content.push(`.monaco-text-button:hover, .monaco-text-button:focus { background-color: ${styles.buttonHoverBackground} !important; }`); - } - - styleTag.innerHTML = content.join('\n'); - document.head.appendChild(styleTag); - document.body.style.color = styles.color || ''; - } - - private handleExtensionData(extensions: IssueReporterExtensionData[]) { - const { nonThemes, themes } = collections.groupBy(extensions, ext => { - return ext.isTheme ? 'themes' : 'nonThemes'; - }); - - const numberOfThemeExtesions = themes && themes.length; - this.issueReporterModel.update({ numberOfThemeExtesions, enabledNonThemeExtesions: nonThemes, allExtensions: extensions }); - this.updateExtensionTable(nonThemes, numberOfThemeExtesions); - - if (this.environmentService.disableExtensions || extensions.length === 0) { - (this.getElementById('disableExtensions')).disabled = true; - } - - this.updateExtensionSelector(extensions); - } - - 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 { - 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; - } - } - - private setEventHandlers(): void { - this.addEventListener('issue-type', 'change', (event: Event) => { - const issueType = parseInt((event.target).value); - this.issueReporterModel.update({ issueType: issueType }); - if (issueType === IssueType.PerformanceIssue && !this.receivedPerformanceInfo) { - ipcRenderer.send('vscode:issuePerformanceInfoRequest'); - } - this.updatePreviewButtonState(); - this.setSourceOptions(); - this.render(); - }); - - (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'] as const).forEach(elementId => { - this.addEventListener(elementId, 'click', (event: Event) => { - event.stopPropagation(); - this.issueReporterModel.update({ [elementId]: !this.issueReporterModel.getData()[elementId] }); - }); - }); - - const showInfoElements = document.getElementsByClassName('showInfo'); - for (let i = 0; i < showInfoElements.length; i++) { - const showInfo = showInfoElements.item(i); - showInfo!.addEventListener('click', (e: MouseEvent) => { - e.preventDefault(); - const label = (e.target); - if (label) { - const containingElement = label.parentElement && label.parentElement.parentElement; - const info = containingElement && containingElement.lastElementChild; - if (info && info.classList.contains('hidden')) { - show(info); - label.textContent = localize('hide', "hide"); - } else { - hide(info); - label.textContent = localize('show', "show"); - } - } - }); - } - - this.addEventListener('issue-source', 'change', (e: Event) => { - const value = (e.target).value; - const problemSourceHelpText = this.getElementById('problem-source-help-text')!; - if (value === '') { - this.issueReporterModel.update({ fileOnExtension: undefined }); - show(problemSourceHelpText); - this.clearSearchResults(); - this.render(); - return; - } else { - hide(problemSourceHelpText); - } - - const fileOnExtension = JSON.parse(value); - this.issueReporterModel.update({ fileOnExtension: fileOnExtension }); - this.render(); - - const title = (this.getElementById('issue-title')).value; - if (fileOnExtension) { - this.searchExtensionIssues(title); - } else { - const description = this.issueReporterModel.getData().issueDescription; - this.searchVSCodeIssues(title, description); - } - }); - - this.addEventListener('description', 'input', (e: Event) => { - const issueDescription = (e.target).value; - this.issueReporterModel.update({ issueDescription }); - - // Only search for extension issues on title change - if (this.issueReporterModel.fileOnExtension() === false) { - const title = (this.getElementById('issue-title')).value; - this.searchVSCodeIssues(title, issueDescription); - } - }); - - this.addEventListener('issue-title', 'input', (e: Event) => { - const title = (e.target).value; - const lengthValidationMessage = this.getElementById('issue-title-length-validation-error'); - if (title && this.getIssueUrlWithTitle(title).length > MAX_URL_LENGTH) { - show(lengthValidationMessage); - } else { - hide(lengthValidationMessage); - } - - const fileOnExtension = this.issueReporterModel.fileOnExtension(); - if (fileOnExtension === undefined) { - return; - } - - if (fileOnExtension) { - this.searchExtensionIssues(title); - } else { - const description = this.issueReporterModel.getData().issueDescription; - this.searchVSCodeIssues(title, description); - } - }); - - this.previewButton.onDidClick(() => this.createIssue()); - - function sendWorkbenchCommand(commandId: string) { - ipcRenderer.send('vscode:workbenchCommand', { id: commandId, from: 'issueReporter' }); - } - - this.addEventListener('disableExtensions', 'click', () => { - sendWorkbenchCommand('workbench.action.reloadWindowWithExtensionsDisabled'); - }); - - this.addEventListener('disableExtensions', 'keydown', (e: KeyboardEvent) => { - e.stopPropagation(); - if (e.keyCode === 13 || e.keyCode === 32) { - sendWorkbenchCommand('workbench.extensions.action.disableAll'); - sendWorkbenchCommand('workbench.action.reloadWindow'); - } - }); - - document.onkeydown = async (e: KeyboardEvent) => { - const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey; - // Cmd/Ctrl+Enter previews issue and closes window - if (cmdOrCtrlKey && e.keyCode === 13) { - if (await this.createIssue()) { - ipcRenderer.send('vscode:closeIssueReporter'); - } - } - - // Cmd/Ctrl + w closes issue window - if (cmdOrCtrlKey && e.keyCode === 87) { - e.stopPropagation(); - e.preventDefault(); - - const issueTitle = (this.getElementById('issue-title'))!.value; - const { issueDescription } = this.issueReporterModel.getData(); - if (!this.hasBeenSubmitted && (issueTitle || issueDescription)) { - ipcRenderer.send('vscode:issueReporterConfirmClose'); - } else { - ipcRenderer.send('vscode:closeIssueReporter'); - } - } - - // Cmd/Ctrl + zooms in - if (cmdOrCtrlKey && e.keyCode === 187) { - this.applyZoom(webFrame.getZoomLevel() + 1); - } - - // Cmd/Ctrl - zooms out - if (cmdOrCtrlKey && e.keyCode === 189) { - this.applyZoom(webFrame.getZoomLevel() - 1); - } - - // With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac - // Manually perform the selection - if (platform.isMacintosh) { - if (cmdOrCtrlKey && e.keyCode === 65 && e.target) { - if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) { - (e.target).select(); - } - } - } - }; - } - - private updatePreviewButtonState() { - if (this.isPreviewEnabled()) { - this.previewButton.label = localize('previewOnGitHub', "Preview on GitHub"); - this.previewButton.enabled = true; - } else { - this.previewButton.enabled = false; - this.previewButton.label = localize('loadingData', "Loading data..."); - } - } - - private isPreviewEnabled() { - const issueType = this.issueReporterModel.getData().issueType; - if (issueType === IssueType.Bug && this.receivedSystemInfo) { - return true; - } - - if (issueType === IssueType.PerformanceIssue && this.receivedSystemInfo && this.receivedPerformanceInfo) { - return true; - } - - if (issueType === IssueType.FeatureRequest) { - return true; - } - - if (issueType === IssueType.SettingsSearchIssue) { - return true; - } - - return false; - } - - private getExtensionRepositoryUrl(): string | undefined { - const selectedExtension = this.issueReporterModel.getData().selectedExtension; - return selectedExtension && selectedExtension.repositoryUrl; - } - - private getExtensionBugsUrl(): string | undefined { - const selectedExtension = this.issueReporterModel.getData().selectedExtension; - return selectedExtension && selectedExtension.bugsUrl; - } - - private searchVSCodeIssues(title: string, issueDescription?: string): void { - if (title) { - this.searchDuplicates(title, issueDescription); - } else { - this.clearSearchResults(); - } - } - - private searchExtensionIssues(title: string): void { - const url = this.getExtensionGitHubUrl(); - if (title) { - const matches = /^https?:\/\/github\.com\/(.*)/.exec(url); - if (matches && matches.length) { - const repo = matches[1]; - return this.searchGitHub(repo, title); - } - - // If the extension has no repository, display empty search results - if (this.issueReporterModel.getData().selectedExtension) { - this.clearSearchResults(); - return this.displaySearchResults([]); - - } - } - - this.clearSearchResults(); - } - - private clearSearchResults(): void { - const similarIssues = this.getElementById('similar-issues')!; - similarIssues.innerHTML = ''; - this.numberOfSearchResultsDisplayed = 0; - } - - @debounce(300) - private searchGitHub(repo: string, title: string): void { - const query = `is:issue+repo:${repo}+${title}`; - const similarIssues = this.getElementById('similar-issues')!; - - window.fetch(`https://api.github.com/search/issues?q=${query}`).then((response) => { - response.json().then(result => { - similarIssues.innerHTML = ''; - if (result && result.items) { - this.displaySearchResults(result.items); - } else { - // If the items property isn't present, the rate limit has been hit - const message = $('div.list-title'); - message.textContent = localize('rateLimited', "GitHub query limit exceeded. Please wait."); - similarIssues.appendChild(message); - - const resetTime = response.headers.get('X-RateLimit-Reset'); - const timeToWait = resetTime ? parseInt(resetTime) - Math.floor(Date.now() / 1000) : 1; - if (this.shouldQueueSearch) { - this.shouldQueueSearch = false; - setTimeout(() => { - this.searchGitHub(repo, title); - this.shouldQueueSearch = true; - }, timeToWait * 1000); - } - } - }).catch(e => { - this.logSearchError(e); - }); - }).catch(e => { - this.logSearchError(e); - }); - } - - @debounce(300) - private searchDuplicates(title: string, body?: string): void { - const url = 'https://vscode-probot.westus.cloudapp.azure.com:7890/duplicate_candidates'; - const init = { - method: 'POST', - body: JSON.stringify({ - title, - body - }), - headers: new Headers({ - 'Content-Type': 'application/json' - }) - }; - - window.fetch(url, init).then((response) => { - response.json().then(result => { - this.clearSearchResults(); - - if (result && result.candidates) { - this.displaySearchResults(result.candidates); - } else { - throw new Error('Unexpected response, no candidates property'); - } - }).catch((error) => { - this.logSearchError(error); - }); - }).catch((error) => { - this.logSearchError(error); - }); - } - - private displaySearchResults(results: SearchResult[]) { - const similarIssues = this.getElementById('similar-issues')!; - if (results.length) { - const issues = $('div.issues-container'); - const issuesText = $('div.list-title'); - issuesText.textContent = localize('similarIssues', "Similar issues"); - - this.numberOfSearchResultsDisplayed = results.length < 5 ? results.length : 5; - for (let i = 0; i < this.numberOfSearchResultsDisplayed; i++) { - const issue = results[i]; - const link = $('a.issue-link', { href: issue.html_url }); - link.textContent = issue.title; - link.title = issue.title; - link.addEventListener('click', (e) => this.openLink(e)); - link.addEventListener('auxclick', (e) => this.openLink(e)); - - let issueState: HTMLElement; - let item: HTMLElement; - if (issue.state) { - issueState = $('span.issue-state'); - - const issueIcon = $('span.issue-icon'); - const codicon = new CodiconLabel(issueIcon); - codicon.text = issue.state === 'open' ? '$(issue-opened)' : '$(issue-closed)'; - - const issueStateLabel = $('span.issue-state.label'); - issueStateLabel.textContent = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed"); - - issueState.title = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed"); - issueState.appendChild(issueIcon); - issueState.appendChild(issueStateLabel); - - item = $('div.issue', {}, issueState, link); - } else { - item = $('div.issue', {}, link); - } - - issues.appendChild(item); - } - - similarIssues.appendChild(issuesText); - similarIssues.appendChild(issues); - } else { - const message = $('div.list-title'); - message.textContent = localize('noSimilarIssues', "No similar issues found"); - similarIssues.appendChild(message); - } - } - - 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 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'); - } - - typeSelect.value = issueType.toString(); - - this.setSourceOptions(); - } - - private makeOption(value: string, description: string, disabled: boolean): HTMLOptionElement { - const option: HTMLOptionElement = document.createElement('option'); - option.disabled = disabled; - option.value = value; - option.textContent = description; - - return option; - } - - private setSourceOptions(): void { - const sourceSelect = this.getElementById('issue-source')! as HTMLSelectElement; - const { issueType, fileOnExtension } = this.issueReporterModel.getData(); - let selected = sourceSelect.selectedIndex; - if (selected === -1 && fileOnExtension !== undefined) { - selected = fileOnExtension ? 2 : 1; - } - - sourceSelect.innerHTML = ''; - if (issueType === IssueType.FeatureRequest) { - sourceSelect.append(...[ - this.makeOption('', localize('selectSource', "Select source"), true), - this.makeOption('false', localize('vscode', "Visual Studio Code"), false), - this.makeOption('true', localize('extension', "An extension"), false) - ]); - } else { - sourceSelect.append(...[ - this.makeOption('', localize('selectSource', "Select source"), true), - this.makeOption('false', localize('vscode', "Visual Studio Code"), false), - this.makeOption('true', localize('extension', "An extension"), false), - this.makeOption('', localize('unknown', "Don't Know"), false) - ]); - } - - if (selected !== -1 && selected < sourceSelect.options.length) { - sourceSelect.selectedIndex = selected; - } else { - sourceSelect.selectedIndex = 0; - hide(this.getElementById('problem-source-help-text')); - } - } - - private renderBlocks(): void { - // Depending on Issue Type, we render different blocks and text - const { issueType, fileOnExtension } = this.issueReporterModel.getData(); - const blockContainer = this.getElementById('block-container'); - const systemBlock = document.querySelector('.block-system'); - const processBlock = document.querySelector('.block-process'); - const workspaceBlock = document.querySelector('.block-workspace'); - const extensionsBlock = document.querySelector('.block-extensions'); - const searchedExtensionsBlock = document.querySelector('.block-searchedExtensions'); - const settingsSearchResultsBlock = document.querySelector('.block-settingsSearchResults'); - - const problemSource = this.getElementById('problem-source')!; - const descriptionTitle = this.getElementById('issue-description-label')!; - const descriptionSubtitle = this.getElementById('issue-description-subtitle')!; - const extensionSelector = this.getElementById('extension-selection')!; - - // Hide all by default - hide(blockContainer); - hide(systemBlock); - hide(processBlock); - hide(workspaceBlock); - hide(extensionsBlock); - hide(searchedExtensionsBlock); - hide(settingsSearchResultsBlock); - hide(problemSource); - hide(extensionSelector); - - if (issueType === IssueType.Bug) { - show(blockContainer); - show(systemBlock); - show(problemSource); - - if (fileOnExtension) { - show(extensionSelector); - } 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."); - } else if (issueType === IssueType.PerformanceIssue) { - show(blockContainer); - show(systemBlock); - show(processBlock); - show(workspaceBlock); - show(problemSource); - - if (fileOnExtension) { - show(extensionSelector); - } else { - 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."); - } 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."); - 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)); - if (!inputElement.value) { - inputElement.classList.add('invalid-input'); - return false; - } else { - inputElement.classList.remove('invalid-input'); - return true; - } - } - - private validateInputs(): boolean { - let isValid = true; - ['issue-title', 'description', 'issue-source'].forEach(elementId => { - isValid = this.validateInput(elementId) && isValid; - }); - - if (this.issueReporterModel.fileOnExtension()) { - isValid = this.validateInput('extension-selector') && isValid; - } - - return isValid; - } - - private async createIssue(): Promise { - if (!this.validateInputs()) { - // If inputs are invalid, set focus to the first one and add listeners on them - // to detect further changes - const invalidInput = document.getElementsByClassName('invalid-input'); - if (invalidInput.length) { - (invalidInput[0]).focus(); - } - - this.addEventListener('issue-title', 'input', _ => { - this.validateInput('issue-title'); - }); - - this.addEventListener('description', 'input', _ => { - this.validateInput('description'); - }); - - this.addEventListener('issue-source', 'change', _ => { - this.validateInput('issue-source'); - }); - - if (this.issueReporterModel.fileOnExtension()) { - this.addEventListener('extension-selector', 'change', _ => { - this.validateInput('extension-selector'); - }); - } - - 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); - const issueBody = this.issueReporterModel.serialize(); - let url = baseUrl + `&body=${encodeURIComponent(issueBody)}`; - - if (url.length > MAX_URL_LENGTH) { - try { - url = await this.writeToClipboard(baseUrl, issueBody); - } catch (_) { - return false; - } - } - - ipcRenderer.send('vscode:openExternal', url); - return true; - } - - private async writeToClipboard(baseUrl: string, issueBody: string): Promise { - return new Promise((resolve, reject) => { - ipcRenderer.once('vscode:issueReporterClipboardResponse', (_: unknown, shouldWrite: boolean) => { - if (shouldWrite) { - clipboard.writeText(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(); - } - }); - - ipcRenderer.send('vscode:issueReporterClipboard'); - }); - } - - private getExtensionGitHubUrl(): string { - let repositoryUrl = ''; - const bugsUrl = this.getExtensionBugsUrl(); - const extensionUrl = this.getExtensionRepositoryUrl(); - // If given, try to match the extension's bug url - if (bugsUrl && bugsUrl.match(/^https?:\/\/github\.com\/(.*)/)) { - repositoryUrl = normalizeGitHubUrl(bugsUrl); - } else if (extensionUrl && extensionUrl.match(/^https?:\/\/github\.com\/(.*)/)) { - repositoryUrl = normalizeGitHubUrl(extensionUrl); - } - - return repositoryUrl; - } - - private getIssueUrlWithTitle(issueTitle: string): string { - let repositoryUrl = product.reportIssueUrl; - if (this.issueReporterModel.fileOnExtension()) { - const extensionGitHubUrl = this.getExtensionGitHubUrl(); - if (extensionGitHubUrl) { - repositoryUrl = extensionGitHubUrl + '/issues/new'; - } - } - - const queryStringPrefix = product.reportIssueUrl && product.reportIssueUrl.indexOf('?') === -1 ? '?' : '&'; - return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`; - } - - private updateSystemInfo(state: IssueReporterModelData) { - 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}
    `; - - systemInfo.remoteData.forEach(remote => { - if (isRemoteDiagnosticError(remote)) { - renderedData += ` -
    - - - -
    Remote${remote.hostName}
    ${remote.errorMessage}
    `; - } else { - renderedData += ` -
    - - - - - - -
    Remote${remote.hostName}
    OS${remote.machineInfo.os}
    CPUs${remote.machineInfo.cpus}
    Memory (System)${remote.machineInfo.memory}
    VM${remote.machineInfo.vmHint}
    `; - } - }); - - target.innerHTML = renderedData; - } - } - - private updateExtensionSelector(extensions: IssueReporterExtensionData[]): void { - interface IOption { - name: string; - id: string; - } - - const extensionOptions: IOption[] = extensions.map(extension => { - return { - name: extension.displayName || extension.name || '', - id: extension.id - }; - }); - - // Sort extensions by name - extensionOptions.sort((a, b) => { - const aName = a.name.toLowerCase(); - const bName = b.name.toLowerCase(); - if (aName > bName) { - return 1; - } - - if (aName < bName) { - return -1; - } - - return 0; - }); - - const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData) => { - const selected = selectedExtension && extension.id === selectedExtension.id; - return ``; - }; - - const extensionsSelector = this.getElementById('extension-selector'); - if (extensionsSelector) { - const { selectedExtension } = this.issueReporterModel.getData(); - extensionsSelector.innerHTML = '' + extensionOptions.map(extension => makeOption(extension, selectedExtension)).join('\n'); - - this.addEventListener('extension-selector', 'change', (e: Event) => { - const selectedExtensionId = (e.target).value; - const extensions = this.issueReporterModel.getData().allExtensions; - const matches = extensions.filter(extension => extension.id === selectedExtensionId); - if (matches.length) { - this.issueReporterModel.update({ selectedExtension: matches[0] }); - - const title = (this.getElementById('issue-title')).value; - this.searchExtensionIssues(title); - } else { - this.issueReporterModel.update({ selectedExtension: undefined }); - this.clearSearchResults(); - } - }); - } - } - - private updateProcessInfo(state: IssueReporterModelData) { - const target = document.querySelector('.block-process .block-info'); - if (target) { - target.innerHTML = `${state.processInfo}`; - } - } - - private updateWorkspaceInfo(state: IssueReporterModelData) { - document.querySelector('.block-workspace .block-info code')!.textContent = '\n' + state.workspaceInfo; - } - - private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { - const target = document.querySelector('.block-extensions .block-info'); - if (target) { - if (this.environmentService.disableExtensions) { - target.innerHTML = localize('disabledExtensions', "Extensions are disabled"); - return; - } - - const themeExclusionStr = numThemeExtensions ? `\n(${numThemeExtensions} theme extensions excluded)` : ''; - extensions = extensions || []; - - if (!extensions.length) { - target.innerHTML = 'Extensions: none' + themeExclusionStr; - return; - } - - const table = this.getExtensionTableHtml(extensions); - target.innerHTML = `${table}
    ${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 openLink(event: MouseEvent): void { - event.preventDefault(); - event.stopPropagation(); - // Exclude right click - if (event.which < 3) { - shell.openExternal((event.target).href); - this.telemetryService.publicLog2('issueReporterViewSimilarIssue'); - } - } - - private getElementById(elementId: string): HTMLElement | undefined { - const element = document.getElementById(elementId); - if (element) { - return element; - } else { - const error = new Error(`${elementId} not found.`); - this.logService.error(error); - type IssueReporterGetElementErrorClassification = { - message: { classification: 'CallstackOrException', purpose: 'PerformanceAndHealth' }; - }; - type IssueReporterGetElementErrorEvent = { - message: string; - }; - this.telemetryService.publicLog2('issueReporterGetElementError', { message: error.message }); - - return undefined; - } - } - - private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { - const element = this.getElementById(elementId); - if (element) { - element.addEventListener(eventType, handler); - } - } -} - -// helper functions - -function hide(el: Element | undefined | null) { - if (el) { - el.classList.add('hidden'); - } -} -function show(el: Element | undefined | null) { - if (el) { - el.classList.remove('hidden'); - } -} diff --git a/src/vs/code/electron-browser/issue/media/issueReporter.css b/src/vs/code/electron-browser/issue/media/issueReporter.css deleted file mode 100644 index 78e41309e488a..0000000000000 --- a/src/vs/code/electron-browser/issue/media/issueReporter.css +++ /dev/null @@ -1,373 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * Table - */ - -table { - width: 100%; - max-width: 100%; - background-color: transparent; - border-collapse: collapse; -} -th { - vertical-align: bottom; - border-bottom: 1px solid; - padding: 5px; - text-align: inherit; -} -td { - padding: 5px; - vertical-align: top; -} - -tr td:first-child { - width: 30%; -} - -label { - user-select: none; -} - -.block-settingsSearchResults-details { - padding-bottom: .5rem; -} - -.block-settingsSearchResults-details > div { - padding: .5rem .75rem; -} - -.section { - margin-bottom: .5em; -} - -/** - * Forms - */ -input[type="text"], textarea { - display: block; - width: 100%; - padding: .375rem .75rem; - font-size: 1rem; - line-height: 1.5; - color: #495057; - background-color: #fff; - border: 1px solid #ced4da; -} - -textarea { - overflow: auto; - resize: vertical; -} - -/** - * Button - */ - -.monaco-text-button { - display: block; - width: auto; - padding: 4px 10px; - align-self: flex-end; - margin-bottom: 10px; -} - -select { - height: calc(2.25rem + 2px); - display: inline-block; - padding: 3px 3px; - font-size: 14px; - line-height: 1.5; - color: #495057; - background-color: #fff; - border: none; -} - -* { - box-sizing: border-box; -} - -textarea, input, select { - font-family: inherit; -} - -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; -} - -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; - overflow-y: scroll; - height: 100%; -} - -.hidden { - display: none; -} - -.block { - font-size: 12px; -} - -.block .block-info { - width: 100%; - font-size: 12px; - overflow: auto; - overflow-wrap: break-word; - margin: 5px; - padding: 10px; -} - -#issue-reporter { - max-width: 85vw; - margin-left: auto; - margin-right: auto; - padding-top: 2em; - padding-bottom: 2em; - display: flex; - flex-direction: column; - height: 100%; -} - -.description-section { - flex-grow: 1; - display: flex; - flex-direction: column; - flex-shrink: 0; -} - -textarea { - flex-grow: 1; - min-height: 150px; -} - -.block-info-text { - display: flex; - flex-grow: 1; -} - -#github-submit-btn { - flex-shrink: 0; - margin-left: auto; - margin-top: 10px; - margin-bottom: 10px; -} - -.two-col { - display: inline-block; - width: 49%; -} - -#vscode-version { - width: 90%; -} - -.input-group { - margin-bottom: 1em; -} - -#extension-selection { - margin-top: 1em; -} - -select, input, textarea { - border: 1px solid transparent; - margin-top: 10px; -} - - -.validation-error { - font-size: 12px; - margin-top: 1em; -} - - -input[type="checkbox"] { - width: auto; - display: inline-block; - margin-top: 0; - vertical-align: middle; - cursor: pointer; -} - -input:disabled { - opacity: 0.6; -} - -.list-title { - margin-top: 1em; - margin-left: 1em; -} - -.instructions { - font-size: 12px; - margin-top: .5em; -} - -a, .workbenchCommand { - cursor: pointer; - border: 1px solid transparent; -} - -.workbenchCommand:disabled { - color: #868e96; - cursor: default -} - -.block-extensions .block-info { - margin-bottom: 1.5em; -} - -/* Default styles, overwritten if a theme is provided */ -input, select, textarea { - background-color: #3c3c3c; - border: none; - color: #cccccc; -} - -a { - color: #CCCCCC; - text-decoration: none; -} - -.invalid-input { - border: 1px solid #be1100; -} - -.required-input, .validation-error { - color: #be1100; -} - -.section .input-group .validation-error { - margin-left: 15%; -} - -.section .inline-form-control, .section .inline-label { - display: inline-block; -} - -.section .inline-label { - width: 95px; -} - -.section .inline-form-control { - width: calc(100% - 100px); -} - -#issue-type { - cursor: pointer; -} - -#similar-issues { - margin-left: 15%; - display: block; -} - -#problem-source-help-text { - margin-left: calc(15% + 1em); -} - -@media (max-width: 950px) { - .section .inline-label { - width: 15%; - } - - #problem-source-help-text { - margin-left: calc(15% + 1em); - } - - .section .inline-form-control { - width: calc(85% - 5px); - } -} - -@media (max-width: 620px) { - .section .inline-label { - display: none !important; - } - - #problem-source-help-text { - margin-left: 1em; - } - - .section .inline-form-control { - width: 100%; - } - - #similar-issues, .section .input-group .validation-error { - margin-left: 0; - } -} - -::-webkit-scrollbar { - width: 14px; -} - -::-webkit-scrollbar-thumb { - min-height: 20px; -} - -::-webkit-scrollbar-corner { - display: none; -} - -.issues-container { - margin-left: 1.5em; - margin-top: .5em; - max-height: 92px; - overflow-y: auto; -} - -.issues-container > .issue { - padding: 4px 0; - display: flex; -} - -.issues-container > .issue > .issue-link { - width: calc(100% - 82px); - overflow: hidden; - padding-top: 3px; - white-space: nowrap; - text-overflow: ellipsis; -} - -.issues-container > .issue > .issue-state .codicon { - width: 16px; -} - -.issues-container > .issue > .issue-state { - width: 77px; - padding: 3px 6px; - margin-right: 5px; - color: #CCCCCC; - background-color: #3c3c3c; - border-radius: .25rem; -} - -.issues-container > .issue .label { - margin-left: 5px; - width: 44px; - text-overflow: ellipsis; - overflow: hidden; -} 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 3a63808c3585c..0000000000000 --- 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 75f73adbb02ca..0000000000000 --- 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 69b0b9a35c952..0000000000000 --- 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 3ef3be23f8ffc..0000000000000 --- 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/processExplorer.js b/src/vs/code/electron-browser/processExplorer/processExplorer.js deleted file mode 100644 index d05816f9915fc..0000000000000 --- a/src/vs/code/electron-browser/processExplorer/processExplorer.js +++ /dev/null @@ -1,13 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 bootstrapWindow = require('../../../../bootstrap-window'); - -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/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts deleted file mode 100644 index 09ccca9b823a3..0000000000000 --- 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/proxy/auth.html b/src/vs/code/electron-browser/proxy/auth.html deleted file mode 100644 index d02876abb940a..0000000000000 --- a/src/vs/code/electron-browser/proxy/auth.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - -

    -
    -

    -
    -

    -

    -

    - - -

    -
    -
    - - - - - diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index 0e79bc811793f..38ee201e341da 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 { IEnvironmentService, 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, + @IEnvironmentService 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 c3a758470fce1..f115dbab0a644 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts @@ -7,7 +7,7 @@ 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 { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/common/product'; export class NodeCachedDataCleaner { @@ -19,7 +19,7 @@ export class NodeCachedDataCleaner { private readonly _disposables = new DisposableStore(); constructor( - @IEnvironmentService private readonly _environmentService: IEnvironmentService + @IEnvironmentService private readonly _environmentService: INativeEnvironmentService ) { this._manageCachedDataSoon(); } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts index a3899b854fbea..e15f9b08aa8b1 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 { IEnvironmentService, 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,7 @@ export class StorageDataCleaner extends Disposable { private static readonly NON_EMPTY_WORKSPACE_ID_LENGTH = 128 / 4; constructor( - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IEnvironmentService private readonly environmentService: INativeEnvironmentService ) { super(); @@ -34,7 +34,7 @@ export class StorageDataCleaner extends Disposable { const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(info => info.backupFolder); // Read all workspace storage folders that exist - return readdir(this.environmentService.workspaceStorageHome).then(storageFolders => { + return readdir(this.environmentService.workspaceStorageHome.fsPath).then(storageFolders => { const deletes: Promise[] = []; storageFolders.forEach(storageFolder => { @@ -43,7 +43,7 @@ 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))); } }); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcess.html b/src/vs/code/electron-browser/sharedProcess/sharedProcess.html index 26890a9fc6bf3..07fd9bd04789b 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 2ecd5f650a83e..54815a8ed1b69 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcess.js +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcess.js @@ -6,8 +6,21 @@ //@ts-check 'use strict'; -const bootstrap = require('../../../../bootstrap'); -const bootstrapWindow = require('../../../../bootstrap-window'); +/** + * @type {{ load: (modules: string[], resultCallback: (result, configuration: object) => any, options?: object) => unknown }} + */ +const bootstrapWindow = (() => { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; +})(); + +/** + * @type {{ avoidMonkeyPatchFromAppInsights: () => void; }} + */ +const bootstrap = (() => { + // @ts-ignore (defined in bootstrap.js) + return window.MonacoBootstrap; +})(); // Avoid Monkey Patches from Application Insights bootstrap.avoidMonkeyPatchFromAppInsights(); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 9ddf3c0eca99d..3d67e73ef30af 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -10,14 +10,15 @@ 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 { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +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'; @@ -26,8 +27,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper 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,34 +35,40 @@ 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, ISettingsMergeService, registerConfiguration, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; -import { UserDataSyncService, UserDataAutoSync } from 'vs/platform/userDataSync/common/userDataSyncService'; -import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; -import { SettingsMergeChannelClient } from 'vs/platform/userDataSync/common/settingsSyncIpc'; -import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; +import { UserDataSyncStoreService, UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, StorageKeysSyncRegistryChannelClient, UserDataSyncMachinesServiceChannel, UserDataSyncAccountServiceChannel, UserDataSyncStoreManagementServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; -import { IAuthTokenService } from 'vs/platform/auth/common/auth'; -import { AuthTokenService } from 'vs/platform/auth/common/authTokenService'; -import { AuthTokenChannel } from 'vs/platform/auth/common/authTokenIpc'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; +import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-browser/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 } from 'vs/platform/userDataSync/common/storageKeys'; +import { ExtensionTipsService } from 'vs/platform/extensionManagement/node/extensionTipsService'; +import { UserDataSyncMachinesService, IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -75,16 +81,20 @@ export function startup(configuration: ISharedProcessConfiguration) { interface ISharedProcessInitData { sharedIPCHandle: string; - args: ParsedArgs; + args: NativeParsedArgs; logLevel: LogLevel; } 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); @@ -102,11 +112,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 EnvironmentService(initData.args); const mainRouter = new StaticRouter(ctx => ctx === 'main'); const loggerClient = new LoggerChannelClient(server.getChannel('logger', mainRouter)); @@ -114,10 +124,30 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat disposables.add(logService); logService.info('main', JSON.stringify(configuration)); - const configurationService = new ConfigurationService(environmentService.settingsResource); + const mainProcessService = new MainProcessService(server, mainRouter); + services.set(IMainProcessService, mainProcessService); + + // 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 StorageKeysSyncRegistryChannelClient(mainProcessService.getChannel('storageKeysSyncRegistryService'))); + services.set(IEnvironmentService, environmentService); services.set(IProductService, { _serviceBrand: undefined, ...product }); services.set(ILogService, logService); @@ -125,24 +155,9 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(ILoggerService, new SyncDescriptor(LoggerService)); - const mainProcessService = new MainProcessService(server, mainRouter); - services.set(IMainProcessService, mainProcessService); - const electronService = createChannelSender(mainProcessService.getChannel('electron'), { context: configuration.windowId }); services.set(IElectronService, electronService); - const activeWindowManager = new ActiveWindowManager(electronService); - 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)); const instantiationService = new InstantiationService(services); @@ -150,14 +165,13 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat let telemetryService: ITelemetryService; instantiationService.invokeFunction(accessor => { const services = new ServiceCollection(); - const environmentService = accessor.get(IEnvironmentService); - const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; + const { appRoot, extensionsPath, 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('==========================================================='); let appInsightsAppender: ITelemetryAppender | null = NullAppender; - if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { + if (!extensionDevelopmentLocationURI && !environmentService.disableTelemetry && product.enableTelemetry) { if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); disposables.add(toDisposable(() => appInsightsAppender!.flush())); // Ensure the AI appender is disposed so that it flushes remaining data @@ -165,6 +179,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const config: ITelemetryServiceConfig = { appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), commonProperties: resolveCommonProperties(product.commit, product.version, configuration.machineId, product.msftInternalDomains, installSourcePath), + sendErrorTelemetry: true, piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] }; @@ -180,13 +195,18 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat 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(IAuthTokenService, new SyncDescriptor(AuthTokenService)); + services.set(IUserDataSyncAccountService, new SyncDescriptor(UserDataSyncAccountService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); - const settingsMergeChannel = server.getChannel('settingsMerge', activeWindowRouter); - services.set(ISettingsMergeService, new SettingsMergeChannelClient(settingsMergeChannel)); + services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', client => client.ctx !== 'main'))); + services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService)); + services.set(IUserDataSyncStoreManagementService, new SyncDescriptor(UserDataSyncStoreManagementService)); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); + services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService)); + services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService)); + services.set(IUserDataSyncResourceEnablementService, new SyncDescriptor(UserDataSyncResourceEnablementService)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); registerConfiguration(); @@ -206,14 +226,30 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const diagnosticsChannel = new DiagnosticsChannel(diagnosticsService); server.registerChannel('diagnostics', diagnosticsChannel); - const authTokenService = accessor.get(IAuthTokenService); - const authTokenChannel = new AuthTokenChannel(authTokenService); - server.registerChannel('authToken', authTokenChannel); + const extensionTipsService = accessor.get(IExtensionTipsService); + const extensionTipsChannel = new ExtensionTipsChannel(extensionTipsService); + server.registerChannel('extensionTipsService', extensionTipsChannel); + + 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(UserDataAutoSyncService); + const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync); + server.registerChannel('userDataAutoSync', userDataAutoSyncChannel); + // clean up deprecated extensions (extensionManagementService as ExtensionManagementService).removeDeprecatedExtensions(); // update localizations cache @@ -224,7 +260,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat instantiationService2.createInstance(LanguagePackCachedDataCleaner), instantiationService2.createInstance(StorageDataCleaner), instantiationService2.createInstance(LogsDataCleaner), - instantiationService2.createInstance(UserDataAutoSync) + userDataAutoSync )); disposables.add(extensionManagementService as ExtensionManagementService); }); @@ -266,13 +302,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 693082bb9edaa..40737461d2930 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 022394edd7586..821e9b29253a6 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -3,16 +3,38 @@ * 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'); +const perf = (function () { + globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; + return { + /** + * @param {string} name + */ + mark(name) { + globalThis.MonacoPerformanceMarks.push(name, Date.now()); + } + }; +})(); + perf.mark('renderer/started'); -const bootstrapWindow = require('../../../../bootstrap-window'); +/** + * @type {{ + * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * }} + */ +const bootstrapWindow = (() => { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; +})(); -// Setup shell environment -process['lazyEnv'] = getLazyEnv(); +// Load environment in parallel to workbench loading to avoid waterfall +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 @@ -23,32 +45,39 @@ bootstrapWindow.load([ 'vs/nls!vs/workbench/workbench.desktop.main', 'vs/css!vs/workbench/workbench.desktop.main' ], - function (workbench, configuration) { + async function (workbench, configuration) { + + // Mark start of workbench perf.mark('didLoadWorkbenchMain'); + performance.mark('workbench-start'); - return process['lazyEnv'].then(function () { - perf.mark('main/startup'); + // Wait for process environment being fully resolved + await whenEnvResolved; - // @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; + perf.mark('main/startup'); + + // @ts-ignore + return require('vs/workbench/electron-browser/desktop.main').main(configuration); }, - beforeRequire: function () { - perf.mark('willLoadWorkbenchMain'); + { + removeDeveloperKeybindingsAfterLoad: true, + canModifyDOM: function (windowConfig) { + showPartsSplash(windowConfig); + }, + beforeLoaderConfig: function (windowConfig, loaderConfig) { + loaderConfig.recordStats = true; + }, + beforeRequire: function () { + perf.mark('willLoadWorkbenchMain'); + } } -}); +); /** * @param {{ * partsSplashPath?: string, - * highContrast?: boolean, + * colorScheme: ('light' | 'dark' | 'hc'), + * autoDetectHighContrast?: boolean, * extensionDevelopmentPath?: string[], * folderUri?: object, * workspace?: object @@ -60,14 +89,15 @@ function showPartsSplash(configuration) { let data; if (typeof configuration.partsSplashPath === 'string') { try { - data = JSON.parse(require('fs').readFileSync(configuration.partsSplashPath, 'utf8')); + data = JSON.parse(require.__$__nodeRequire('fs').readFileSync(configuration.partsSplashPath, 'utf8')); } catch (e) { // ignore } } // 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') { + const isHighContrast = configuration.colorScheme === 'hc' /* ColorScheme.HIGH_CONTRAST */ && configuration.autoDetectHighContrast; + if (data && isHighContrast && data.baseTheme !== 'hc-black') { data = undefined; } @@ -77,20 +107,31 @@ function showPartsSplash(configuration) { } // 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'; + 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); - document.body.className = baseTheme; - style.innerHTML = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`; + 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'; @@ -107,48 +148,31 @@ function showPartsSplash(configuration) { // 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 - splash.innerHTML = ` -
    -
    -
    -
    - `; - } else { - // empty -> speical status bar color, no sidebar - splash.innerHTML = ` -
    -
    -
    - `; + 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'); } - -/** - * @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'); - }); -} diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index e8afc9c7217c9..8fd9ded27b66b 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, shell, Event, 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,21 +22,21 @@ 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 { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; 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 { ITelemetryService, machineIdKey } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, combinedAppender, LogAppender } 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 } from 'vs/base/parts/ipc/common/ipc'; import product from 'vs/platform/product/common/product'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; +import { ActiveWindowManager } from 'vs/platform/windows/electron-main/windowTracker'; import { URI } from 'vs/base/common/uri'; import { hasWorkspaceFileExtension, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesService } from 'vs/platform/workspaces/electron-main/workspacesService'; @@ -44,48 +44,47 @@ 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 { RunOnceScheduler } from 'vs/base/common/async'; +import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; +import { RunOnceScheduler, timeout } 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 { join, sep, posix } from 'vs/base/common/path'; 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 { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/electron-main/extensionHostDebugIpc'; import { IElectronMainService, ElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; 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 { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +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'; 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; @@ -94,7 +93,7 @@ export class CodeApplication extends Disposable { private readonly userEnv: IProcessEnvironment, @IInstantiationService private readonly instantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStateService private readonly stateService: IStateService @@ -128,15 +127,14 @@ export class CodeApplication extends Disposable { // Mac only event: open new window when we get activated if (!hasVisibleWindows && this.windowsMainService) { - this.windowsMainService.openEmptyWindow(OpenContext.DOCK); + 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'); @@ -171,19 +169,20 @@ export class CodeApplication extends Disposable { app.on('web-contents-created', (_event: Event, contents) => { contents.on('will-attach-webview', (event: Event, webPreferences, params) => { - const isValidWebviewSource = (source: string): boolean => { + 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 @@ -191,11 +190,12 @@ export class CodeApplication extends Disposable { webPreferences.nodeIntegration = false; // Verify URLs being loaded - if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) { + // https://github.com/electron/electron/issues/21553 + if (isValidWebviewSource(params.src) && isValidWebviewSource((webPreferences as { preloadURL: string }).preloadURL)) { return; } - delete webPreferences.preloadUrl; + 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'); @@ -214,8 +214,18 @@ export class CodeApplication extends Disposable { shell.openExternal(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) => { @@ -250,22 +260,21 @@ export class CodeApplication extends Disposable { app.on('new-window-for-tab', () => { if (this.windowsMainService) { - this.windowsMainService.openEmptyWindow(OpenContext.DESKTOP); //macOS native tab "+" button + this.windowsMainService.openEmptyWindow({ context: 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); - }); - ipc.on('vscode:fetchShellEnv', async (event: IpcMainEvent) => { const webContents = event.sender; try { const shellEnv = await getShellEnvironment(this.logService, this.environmentService); + + // TODO@sandbox workaround for https://github.com/electron/electron/issues/25119 + if (this.environmentService.sandbox) { + await timeout(100); + } + if (!webContents.isDestroyed()) { webContents.send('vscode:acceptShellEnv', shellEnv); } @@ -287,18 +296,11 @@ 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'); } }); })(); @@ -358,22 +360,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) { @@ -390,7 +399,7 @@ export class CodeApplication extends Disposable { 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) { @@ -398,40 +407,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,13 +447,14 @@ export class CodeApplication extends Disposable { services.set(ISharedProcessMainService, new SyncDescriptor(SharedProcessMainService, [sharedProcess])); services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService)); - const diagnosticsChannel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('diagnostics'))); + const diagnosticsChannel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics'))); services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService, [diagnosticsChannel])); - services.set(IIssueService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); + services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); services.set(IElectronMainService, new SyncDescriptor(ElectronMainService)); + 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); @@ -471,16 +464,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 channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender'))); const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)); 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 { @@ -497,27 +490,27 @@ export class CodeApplication extends Disposable { this.logService.info(`Tracing: waiting for windows to get ready...`); let recordingStopped = false; - const stopRecording = (timeout: boolean) => { + const stopRecording = async (timeout: boolean) => { if (recordingStopped) { return; } recordingStopped = true; // only once - contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => { - 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())); - } - } else { - this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); + const path = await contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`)); + + 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())); } - }); + } else { + this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); + } }; // Wait up to 30s before creating the trace anyways @@ -542,8 +535,8 @@ 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); @@ -559,25 +552,34 @@ 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 storageKeysSyncRegistryService = accessor.get(IStorageKeysSyncRegistryService); + const storageKeysSyncChannel = new StorageKeysSyncRegistryChannel(storageKeysSyncRegistryService); + electronIpcServer.registerChannel('storageKeysSyncRegistryService', storageKeysSyncChannel); + sharedProcessClient.then(client => client.registerChannel('storageKeysSyncRegistryService', storageKeysSyncChannel)); 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) @@ -586,21 +588,76 @@ 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); + // 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 + }); - // hey Ben, we need to convert this `code://file` URI into a `file://` URI - const urisToOpen = [{ fileUri: URI.file(uri.fsPath) }]; + return true; + } - windowsMainService.open({ context: OpenContext.API, cli, urisToOpen, gotoLineMode: 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 + }); - return true; + await window.ready(); + + return urlService.open(uri); } return false; @@ -612,37 +669,13 @@ export class CodeApplication extends Disposable { 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._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; @@ -651,8 +684,21 @@ 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 - if (args['new-window'] && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { + // 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" or "--remote" was used without paths + if ((args['new-window'] || args.remote) && !hasCliArgs && !hasFolderURIs && !hasFileURIs) { return windowsMainService.open({ context, cli: args, @@ -672,7 +718,6 @@ export class CodeApplication extends Disposable { urisToOpen: macOpenFiles.map(file => this.getWindowOpenableFromPathSync(file)), noRecentEntry, waitMarkerFileURI, - gotoLineMode: false, initialStartup: true }); } @@ -690,6 +735,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); @@ -707,13 +809,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 { @@ -725,28 +864,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 45229b3bdd891..dd0d67ae21e5d 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 { URI } from 'vs/base/common/uri'; +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: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).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 url = require.toUrl('vs/code/electron-sandbox/proxy/auth.html'); 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(url); } } diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index bb05eddc6ce4f..d0ae145056701 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 { 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,13 @@ 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 { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { EnvironmentService, xdgRuntimeDir } from 'vs/platform/environment/node/environmentService'; 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'; @@ -42,6 +41,19 @@ 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 { 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 { IStorageKeysSyncRegistryService, StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +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'; class ExpectedError extends Error { readonly isExpected = true; @@ -56,10 +68,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 +98,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) 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 +126,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,10 +146,10 @@ class CodeMain { } } - private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, IProcessEnvironment] { + private createServices(args: NativeParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, IProcessEnvironment, INativeEnvironmentService] { const services = new ServiceCollection(); - const environmentService = new EnvironmentService(args, process.execPath); + const environmentService = new EnvironmentService(args); const instanceEnvironment = this.patchEnvironment(environmentService); // Patch `process.env` with the instance's environment services.set(IEnvironmentService, environmentService); @@ -143,26 +157,34 @@ class CodeMain { 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(IStorageKeysSyncRegistryService, new SyncDescriptor(StorageKeysSyncRegistryService)); + 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: INativeEnvironmentService, 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,7 +196,7 @@ class CodeMain { return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]); } - private patchEnvironment(environmentService: IEnvironmentService): IProcessEnvironment { + private patchEnvironment(environmentService: INativeEnvironmentService): IProcessEnvironment { const instanceEnvironment: IProcessEnvironment = { VSCODE_IPC_HOOK: environmentService.mainIPCHandle }; @@ -186,12 +208,12 @@ class CodeMain { } }); - 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: INativeEnvironmentService, 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 +235,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 +264,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 +280,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,8 +292,9 @@ 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'); @@ -297,7 +315,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 +329,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 +342,7 @@ class CodeMain { return server; } - private handleStartupDataDirError(environmentService: IEnvironmentService, error: NodeJS.ErrnoException): void { + private handleStartupDataDirError(environmentService: INativeEnvironmentService, error: NodeJS.ErrnoException): void { if (error.code === 'EACCES' || error.code === 'EPERM') { const directories = [environmentService.userDataPath]; @@ -349,7 +362,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 +413,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 fc716c82be1c5..86ebb7ff93257 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assign } from 'vs/base/common/objects'; +import { URI } from 'vs/base/common/uri'; import { memoize } from 'vs/base/common/decorators'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; 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'; @@ -21,34 +21,44 @@ 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, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @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: URI.parse(require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js')).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); @@ -98,16 +108,19 @@ 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() }); - 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 +135,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 2abbdf31166ae..39444b538395d 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 } 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 { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; 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 { 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,14 +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'; - -const RUN_TEXTMATE_IN_WORKER = false; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; export interface IWindowCreationOptions { state: IWindowState; @@ -57,21 +59,47 @@ 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 = 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 _onLoad = this._register(new Emitter()); + 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: CommonEvent = this._onClose.event; + readonly onClose = this._onClose.event; private readonly _onDestroy = this._register(new Emitter()); - readonly onDestroy: CommonEvent = this._onDestroy.event; - - private readonly _onLoad = this._register(new Emitter()); - readonly onLoad: CommonEvent = this._onLoad.event; + readonly onDestroy = this._onDestroy.event; private hiddenTitleBarStyle: boolean | undefined; private showTimeoutHandle: NodeJS.Timeout | undefined; @@ -79,27 +107,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, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @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(); @@ -113,10 +148,13 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Load window state const [state, hasMultipleDisplays] = this.restoreWindowState(config.state); this.windowState = state; + this.logService.trace('window#ctor: using window state', state); // 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, @@ -128,23 +166,37 @@ export class CodeWindow extends Disposable implements ICodeWindow { show: !isFullscreenOrMaximized, title: product.nameLong, webPreferences: { - // By default if Code is in the background, intervals and timeouts get throttled, so we - // want to enforce that Code stays in the foreground. This triggers a disable_hidden_ - // flag that Electron provides via patch: - // https://github.com/electron/libchromiumcontent/blob/master/patches/common/chromium/disable_hidden.patch - backgroundThrottling: false, - nodeIntegration: true, - nodeIntegrationInWorker: RUN_TEXTMATE_IN_WORKER, - webviewTag: true + preload: URI.parse(this.doGetPreloadUrl()).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 + } } }; + // Apply icon to window + // Linux: always + // Windows: only when running out of sources, otherwise an icon is set by us on the executable if (isLinux) { - options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s) + options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); + } else if (isWindows && !this.environmentService.isBuilt) { + 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 } @@ -175,6 +227,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 } @@ -221,14 +278,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; } @@ -242,6 +303,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); @@ -258,7 +321,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 >6.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; } @@ -287,6 +377,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { while (this.whenReadyCallbacks.length) { this.whenReadyCallbacks.pop()!(this); } + + // Events + this._onReady.fire(); } ready(): Promise { @@ -322,7 +415,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 @@ -336,7 +429,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 }); } } @@ -345,14 +438,14 @@ export class CodeWindow extends Disposable implements ICodeWindow { }); this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => { - const responseHeaders = details.responseHeaders as { [key: string]: string[] }; + const responseHeaders = details.responseHeaders as Record; - const contentType: string[] = (responseHeaders['content-type'] || responseHeaders['Content-Type']); + const contentType = (responseHeaders['content-type'] || responseHeaders['Content-Type']); if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { return callback({ cancel: true }); } - return callback({ cancel: false, responseHeaders }); + return callback({ cancel: false }); }); // Remember that we loaded @@ -372,22 +465,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))); @@ -438,20 +543,24 @@ 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 => { - const requestHeaders = objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined }; - if (!this.configurationService.getValue('extensions.disableExperimentalAzureSearch')) { - requestHeaders['Cookie'] = `${requestHeaders['Cookie'] ? requestHeaders['Cookie'] + ';' : ''}EnableExternalSearchForVSCode=true`; - } - cb({ cancel: false, requestHeaders }); - }); - }); + this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => + 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 }; }; @@ -495,11 +604,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 => { @@ -536,6 +652,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 { @@ -544,7 +681,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 @@ -563,15 +700,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); } } @@ -594,7 +731,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); @@ -604,7 +741,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); @@ -631,7 +768,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; @@ -649,11 +786,8 @@ 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 && systemPreferences.isInvertedColorScheme(); + windowConfiguration.colorScheme = (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) ? ColorScheme.HIGH_CONTRAST : nativeTheme.shouldUseDarkColors ? ColorScheme.DARK : ColorScheme.LIGHT; + windowConfiguration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true; windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; // Title style related @@ -667,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 } } @@ -694,7 +828,18 @@ 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 `${require.toUrl(workbench)}?config=${encodeURIComponent(JSON.stringify(config))}`; + } + + private doGetPreloadUrl(): string { + return require.toUrl('vs/base/parts/sandbox/electron-browser/preload.js'); } serializeWindowState(): IWindowState { @@ -704,7 +849,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(); @@ -778,45 +930,79 @@ export class CodeWindow extends Disposable implements ICodeWindow { } private validateWindowState(state: IWindowState, displays: Display[]): IWindowState | undefined { + this.logService.trace(`window#validateWindowState: validating window state on ${displays.length} display(s)`, state); + if (typeof state.x !== 'number' || typeof state.y !== 'number' || typeof state.width !== 'number' || typeof state.height !== 'number' ) { + this.logService.trace('window#validateWindowState: unexpected type of state values'); return undefined; } if (state.width <= 0 || state.height <= 0) { + this.logService.trace('window#validateWindowState: unexpected negative values'); return undefined; } // Single Monitor: be strict about x/y positioning + // macOS & Linux: these OS seem to be pretty good in ensuring that a window is never outside of it's bounds. + // Windows: it is possible to have a window with a size that makes it fall out of the window. our strategy + // is to try as much as possible to keep the window in the monitor bounds. we are not as strict as + // macOS and Linux and allow the window to exceed the monitor bounds as long as the window is still + // some pixels (128) visible on the screen for the user to drag it back. if (displays.length === 1) { const displayWorkingArea = this.getWorkingArea(displays[0]); if (displayWorkingArea) { - if (state.x < displayWorkingArea.x) { - state.x = displayWorkingArea.x; // prevent window from falling out of the screen to the left - } + this.logService.trace('window#validateWindowState: 1 monitor working area', displayWorkingArea); - if (state.y < displayWorkingArea.y) { - state.y = displayWorkingArea.y; // prevent window from falling out of the screen to the top - } + function ensureStateInDisplayWorkingArea(): void { + if (!state || typeof state.x !== 'number' || typeof state.y !== 'number' || !displayWorkingArea) { + return; + } - if (state.x > (displayWorkingArea.x + displayWorkingArea.width)) { - state.x = displayWorkingArea.x; // prevent window from falling out of the screen to the right - } + if (state.x < displayWorkingArea.x) { + // prevent window from falling out of the screen to the left + state.x = displayWorkingArea.x; + } - if (state.y > (displayWorkingArea.y + displayWorkingArea.height)) { - state.y = displayWorkingArea.y; // prevent window from falling out of the screen to the bottom + if (state.y < displayWorkingArea.y) { + // prevent window from falling out of the screen to the top + state.y = displayWorkingArea.y; + } } + // ensure state is not outside display working area (top, left) + ensureStateInDisplayWorkingArea(); + if (state.width > displayWorkingArea.width) { - state.width = displayWorkingArea.width; // prevent window from exceeding display bounds width + // prevent window from exceeding display bounds width + state.width = displayWorkingArea.width; } if (state.height > displayWorkingArea.height) { - state.height = displayWorkingArea.height; // prevent window from exceeding display bounds height + // prevent window from exceeding display bounds height + state.height = displayWorkingArea.height; } + + if (state.x > (displayWorkingArea.x + displayWorkingArea.width - 128)) { + // prevent window from falling out of the screen to the right with + // 128px margin by positioning the window to the far right edge of + // the screen + state.x = displayWorkingArea.x + displayWorkingArea.width - state.width; + } + + if (state.y > (displayWorkingArea.y + displayWorkingArea.height - 128)) { + // prevent window from falling out of the screen to the bottom with + // 128px margin by positioning the window to the far bottom edge of + // the screen + state.y = displayWorkingArea.y + displayWorkingArea.height - state.height; + } + + // again ensure state is not outside display working area + // (it may have changed from the previous validation step) + ensureStateInDisplayWorkingArea(); } return state; @@ -824,8 +1010,10 @@ 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'); + const defaults = defaultWindowState(WindowMode.Fullscreen); // make sure we have good values when the user restores the window defaults.x = display.bounds.x; // carefull to use displays x/y position so that the window ends up on the correct monitor defaults.y = display.bounds.y; @@ -834,18 +1022,28 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - // Multi Monitor (non-fullscreen): be less strict because metrics can be crazy - const bounds = { x: state.x, y: state.y, width: state.width, height: state.height }; - const display = screen.getDisplayMatching(bounds); - const displayWorkingArea = this.getWorkingArea(display); + // Multi Monitor (non-fullscreen): ensure window is within display bounds + 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 - bounds.x < displayWorkingArea.x + displayWorkingArea.width && // prevent window from falling out of the screen to the right - bounds.y < displayWorkingArea.y + displayWorkingArea.height && // prevent window from falling out of the screen to the bottom - bounds.x + bounds.width > displayWorkingArea.x && // prevent window from falling out of the screen to the left - bounds.y + bounds.height > displayWorkingArea.y // prevent window from falling out of the scree nto the top + state.x + state.width > displayWorkingArea.x && // prevent window from falling out of the screen to the left + state.y + state.height > displayWorkingArea.y && // prevent window from falling out of the screen to the top + state.x < displayWorkingArea.x + displayWorkingArea.width && // prevent window from falling out of the screen to the right + state.y < displayWorkingArea.y + displayWorkingArea.height // prevent window from falling out of the screen to the bottom ) { + this.logService.trace('window#validateWindowState: multi-monitor working area', displayWorkingArea); + return state; } @@ -975,22 +1173,22 @@ export class CodeWindow extends Disposable implements ICodeWindow { switch (visibility) { case ('default'): this._win.setMenuBarVisibility(!isFullscreen); - this._win.setAutoHideMenuBar(isFullscreen); + this._win.autoHideMenuBar = isFullscreen; break; case ('visible'): this._win.setMenuBarVisibility(true); - this._win.setAutoHideMenuBar(false); + this._win.autoHideMenuBar = false; break; case ('toggle'): this._win.setMenuBarVisibility(false); - this._win.setAutoHideMenuBar(true); + this._win.autoHideMenuBar = true; break; case ('hidden'): this._win.setMenuBarVisibility(false); - this._win.setAutoHideMenuBar(false); + this._win.autoHideMenuBar = false; break; } } @@ -1096,8 +1294,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { private createTouchBarGroupSegments(items: ISerializableCommandAction[] = []): ITouchBarSegment[] { const segments: ITouchBarSegment[] = items.map(item => { let icon: NativeImage | undefined; - if (item.iconLocation && item.iconLocation.dark.scheme === 'file') { - icon = nativeImage.createFromPath(URI.revive(item.iconLocation.dark).fsPath); + if (item.icon && !ThemeIcon.isThemeIcon(item.icon) && item.icon?.dark?.scheme === '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 0000000000000..3a0cb4be742f5 --- /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 0000000000000..6991c2bba2ae3 --- /dev/null +++ b/src/vs/code/electron-sandbox/issue/issueReporter.js @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +/** + * @type {{ load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown }} + */ +const bootstrapWindow = (() => { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; +})(); + +bootstrapWindow.load(['vs/code/electron-sandbox/issue/issueReporterMain'], function (issueReporter, configuration) { + issueReporter.startup(configuration); +}, { forceEnableDeveloperKeybindings: true, disallowReloadKeybinding: true }); diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts new file mode 100644 index 0000000000000..108f02d1abd1d --- /dev/null +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -0,0 +1,1137 @@ +/*--------------------------------------------------------------------------------------------- + * 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/issueReporter'; +import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded +import { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +import { ipcRenderer, process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { applyZoom, zoomIn, zoomOut } from 'vs/platform/windows/electron-sandbox/window'; +import { $, reset, windowOpenNoOpener, addClass } from 'vs/base/browser/dom'; +import { Button } from 'vs/base/browser/ui/button/button'; +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 { 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 { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +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; + +interface SearchResult { + html_url: string; + title: string; + state?: string; +} + +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) { + const platformClass = platform.isWindows ? 'windows' : platform.isLinux ? 'linux' : 'mac'; + addClass(document.body, platformClass); // used by our fonts + + document.body.innerHTML = BaseHtml(); + const issueReporter = new IssueReporter(configuration); + issueReporter.render(); + document.body.style.display = 'block'; + issueReporter.setInitialFocus(); +} + +export class IssueReporter extends Disposable { + private electronService!: IElectronService; + private readonly issueReporterModel: IssueReporterModel; + private numberOfSearchResultsDisplayed = 0; + private receivedSystemInfo = false; + private receivedPerformanceInfo = false; + private shouldQueueSearch = false; + private hasBeenSubmitted = false; + + private readonly previewButton!: Button; + + 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: `${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: !!configuration.disableExtensions, + fileOnExtension: configuration.data.extensionId ? !targetExtension?.isBuiltin : undefined, + selectedExtension: targetExtension, + }); + + const issueReporterElement = this.getElementById('issue-reporter'); + if (issueReporterElement) { + 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.issueReporterModel.update(info); + this.receivedPerformanceInfo = true; + + const state = this.issueReporterModel.getData(); + this.updateProcessInfo(state); + this.updateWorkspaceInfo(state); + this.updatePreviewButtonState(); + }); + + ipcRenderer.on('vscode:issueSystemInfoResponse', (_: unknown, info: SystemInfo) => { + this.issueReporterModel.update({ systemInfo: info }); + this.receivedSystemInfo = true; + + this.updateSystemInfo(this.issueReporterModel.getData()); + this.updatePreviewButtonState(); + }); + + ipcRenderer.send('vscode:issueSystemInfoRequest'); + if (configuration.data.issueType === IssueType.PerformanceIssue) { + ipcRenderer.send('vscode:issuePerformanceInfoRequest'); + } + + if (window.document.documentElement.lang !== 'en') { + show(this.getElementById('english')); + } + + this.setUpTypes(); + this.setEventHandlers(); + applyZoom(configuration.data.zoomLevel); + this.applyStyles(configuration.data.styles); + this.handleExtensionData(configuration.data.enabledExtensions); + } + + render(): void { + this.renderBlocks(); + } + + setInitialFocus() { + const { fileOnExtension } = this.issueReporterModel.getData(); + if (fileOnExtension) { + const issueTitle = document.getElementById('issue-title'); + if (issueTitle) { + issueTitle.focus(); + } + } else { + const issueType = document.getElementById('issue-type'); + if (issueType) { + issueType.focus(); + } + } + } + + private applyStyles(styles: IssueReporterStyles) { + const styleTag = document.createElement('style'); + const content: string[] = []; + + if (styles.inputBackground) { + content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { background-color: ${styles.inputBackground}; }`); + } + + if (styles.inputBorder) { + content.push(`input[type="text"], textarea, select { border: 1px solid ${styles.inputBorder}; }`); + } else { + content.push(`input[type="text"], textarea, select { border: 1px solid transparent; }`); + } + + if (styles.inputForeground) { + content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { color: ${styles.inputForeground}; }`); + } + + if (styles.inputErrorBorder) { + content.push(`.invalid-input, .invalid-input:focus, .validation-error { border: 1px solid ${styles.inputErrorBorder} !important; }`); + content.push(`.required-input { color: ${styles.inputErrorBorder}; }`); + } + + if (styles.inputErrorBackground) { + content.push(`.validation-error { background: ${styles.inputErrorBackground}; }`); + } + + if (styles.inputErrorForeground) { + content.push(`.validation-error { color: ${styles.inputErrorForeground}; }`); + } + + if (styles.inputActiveBorder) { + content.push(`input[type='text']:focus, textarea:focus, select:focus, summary:focus, button:focus, a:focus, .workbenchCommand:focus { border: 1px solid ${styles.inputActiveBorder}; outline-style: none; }`); + } + + if (styles.textLinkColor) { + content.push(`a, .workbenchCommand { color: ${styles.textLinkColor}; }`); + } + + if (styles.textLinkColor) { + content.push(`a { color: ${styles.textLinkColor}; }`); + } + + if (styles.textLinkActiveForeground) { + content.push(`a:hover, .workbenchCommand:hover { color: ${styles.textLinkActiveForeground}; }`); + } + + if (styles.sliderBackgroundColor) { + content.push(`::-webkit-scrollbar-thumb { background-color: ${styles.sliderBackgroundColor}; }`); + } + + if (styles.sliderActiveColor) { + content.push(`::-webkit-scrollbar-thumb:active { background-color: ${styles.sliderActiveColor}; }`); + } + + if (styles.sliderHoverColor) { + content.push(`::--webkit-scrollbar-thumb:hover { background-color: ${styles.sliderHoverColor}; }`); + } + + if (styles.buttonBackground) { + content.push(`.monaco-text-button { background-color: ${styles.buttonBackground} !important; }`); + } + + if (styles.buttonForeground) { + content.push(`.monaco-text-button { color: ${styles.buttonForeground} !important; }`); + } + + if (styles.buttonHoverBackground) { + content.push(`.monaco-text-button:not(.disabled):hover, .monaco-text-button:focus { background-color: ${styles.buttonHoverBackground} !important; }`); + } + + styleTag.textContent = content.join('\n'); + document.head.appendChild(styleTag); + document.body.style.color = styles.color || ''; + } + + private handleExtensionData(extensions: IssueReporterExtensionData[]) { + 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: installedExtensions }); + this.updateExtensionTable(nonThemes, numberOfThemeExtesions); + + if (this.configuration.disableExtensions || installedExtensions.length === 0) { + (this.getElementById('disableExtensions')).disabled = true; + } + + this.updateExtensionSelector(installedExtensions); + } + + private initServices(configuration: IssueReporterConfiguration): void { + const serviceCollection = new ServiceCollection(); + const mainProcessService = new MainProcessService(configuration.windowId); + serviceCollection.set(IMainProcessService, mainProcessService); + + this.electronService = new ElectronService(configuration.windowId, mainProcessService) as IElectronService; + serviceCollection.set(IElectronService, this.electronService); + } + + private setEventHandlers(): void { + this.addEventListener('issue-type', 'change', (event: Event) => { + const issueType = parseInt((event.target).value); + this.issueReporterModel.update({ issueType: issueType }); + if (issueType === IssueType.PerformanceIssue && !this.receivedPerformanceInfo) { + ipcRenderer.send('vscode:issuePerformanceInfoRequest'); + } + this.updatePreviewButtonState(); + this.setSourceOptions(); + this.render(); + }); + + (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeSearchedExtensions', 'includeSettingsSearchDetails'] as const).forEach(elementId => { + this.addEventListener(elementId, 'click', (event: Event) => { + event.stopPropagation(); + this.issueReporterModel.update({ [elementId]: !this.issueReporterModel.getData()[elementId] }); + }); + }); + + const showInfoElements = document.getElementsByClassName('showInfo'); + for (let i = 0; i < showInfoElements.length; i++) { + const showInfo = showInfoElements.item(i)!; + (showInfo as HTMLAnchorElement).addEventListener('click', (e: MouseEvent) => { + e.preventDefault(); + const label = (e.target); + if (label) { + const containingElement = label.parentElement && label.parentElement.parentElement; + const info = containingElement && containingElement.lastElementChild; + if (info && info.classList.contains('hidden')) { + show(info); + label.textContent = localize('hide', "hide"); + } else { + hide(info); + label.textContent = localize('show', "show"); + } + } + }); + } + + this.addEventListener('issue-source', 'change', (e: Event) => { + const value = (e.target).value; + const problemSourceHelpText = this.getElementById('problem-source-help-text')!; + if (value === '') { + this.issueReporterModel.update({ fileOnExtension: undefined }); + show(problemSourceHelpText); + this.clearSearchResults(); + this.render(); + return; + } else { + hide(problemSourceHelpText); + } + + const fileOnExtension = JSON.parse(value); + this.issueReporterModel.update({ fileOnExtension: fileOnExtension }); + this.render(); + + const title = (this.getElementById('issue-title')).value; + if (fileOnExtension) { + this.searchExtensionIssues(title); + } else { + const description = this.issueReporterModel.getData().issueDescription; + this.searchVSCodeIssues(title, description); + } + }); + + this.addEventListener('description', 'input', (e: Event) => { + const issueDescription = (e.target).value; + this.issueReporterModel.update({ issueDescription }); + + // Only search for extension issues on title change + if (this.issueReporterModel.fileOnExtension() === false) { + const title = (this.getElementById('issue-title')).value; + this.searchVSCodeIssues(title, issueDescription); + } + }); + + this.addEventListener('issue-title', 'input', (e: Event) => { + const title = (e.target).value; + const lengthValidationMessage = this.getElementById('issue-title-length-validation-error'); + if (title && this.getIssueUrlWithTitle(title).length > MAX_URL_LENGTH) { + show(lengthValidationMessage); + } else { + hide(lengthValidationMessage); + } + + const fileOnExtension = this.issueReporterModel.fileOnExtension(); + if (fileOnExtension === undefined) { + return; + } + + if (fileOnExtension) { + this.searchExtensionIssues(title); + } else { + const description = this.issueReporterModel.getData().issueDescription; + this.searchVSCodeIssues(title, description); + } + }); + + this.previewButton.onDidClick(() => this.createIssue()); + + function sendWorkbenchCommand(commandId: string) { + ipcRenderer.send('vscode:workbenchCommand', { id: commandId, from: 'issueReporter' }); + } + + this.addEventListener('disableExtensions', 'click', () => { + sendWorkbenchCommand('workbench.action.reloadWindowWithExtensionsDisabled'); + }); + + this.addEventListener('extensionBugsLink', 'click', (e: Event) => { + const url = (e.target).innerText; + windowOpenNoOpener(url); + }); + + this.addEventListener('disableExtensions', 'keydown', (e: Event) => { + e.stopPropagation(); + if ((e as KeyboardEvent).keyCode === 13 || (e as KeyboardEvent).keyCode === 32) { + sendWorkbenchCommand('workbench.extensions.action.disableAll'); + sendWorkbenchCommand('workbench.action.reloadWindow'); + } + }); + + document.onkeydown = async (e: KeyboardEvent) => { + const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey; + // Cmd/Ctrl+Enter previews issue and closes window + if (cmdOrCtrlKey && e.keyCode === 13) { + if (await this.createIssue()) { + ipcRenderer.send('vscode:closeIssueReporter'); + } + } + + // Cmd/Ctrl + w closes issue window + if (cmdOrCtrlKey && e.keyCode === 87) { + e.stopPropagation(); + e.preventDefault(); + + const issueTitle = (this.getElementById('issue-title'))!.value; + const { issueDescription } = this.issueReporterModel.getData(); + if (!this.hasBeenSubmitted && (issueTitle || issueDescription)) { + ipcRenderer.send('vscode:issueReporterConfirmClose'); + } else { + ipcRenderer.send('vscode:closeIssueReporter'); + } + } + + // Cmd/Ctrl + zooms in + if (cmdOrCtrlKey && e.keyCode === 187) { + zoomIn(); + } + + // Cmd/Ctrl - zooms out + if (cmdOrCtrlKey && e.keyCode === 189) { + zoomOut(); + } + + // With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac + // Manually perform the selection + if (platform.isMacintosh) { + if (cmdOrCtrlKey && e.keyCode === 65 && e.target) { + if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) { + (e.target).select(); + } + } + } + }; + } + + private updatePreviewButtonState() { + if (this.isPreviewEnabled()) { + this.previewButton.label = localize('previewOnGitHub', "Preview on GitHub"); + this.previewButton.enabled = true; + } else { + this.previewButton.enabled = false; + this.previewButton.label = localize('loadingData', "Loading data..."); + } + } + + private isPreviewEnabled() { + const issueType = this.issueReporterModel.getData().issueType; + if (issueType === IssueType.Bug && this.receivedSystemInfo) { + return true; + } + + if (issueType === IssueType.PerformanceIssue && this.receivedSystemInfo && this.receivedPerformanceInfo) { + return true; + } + + if (issueType === IssueType.FeatureRequest) { + return true; + } + + return false; + } + + private getExtensionRepositoryUrl(): string | undefined { + const selectedExtension = this.issueReporterModel.getData().selectedExtension; + return selectedExtension && selectedExtension.repositoryUrl; + } + + private getExtensionBugsUrl(): string | undefined { + const selectedExtension = this.issueReporterModel.getData().selectedExtension; + return selectedExtension && selectedExtension.bugsUrl; + } + + private searchVSCodeIssues(title: string, issueDescription?: string): void { + if (title) { + this.searchDuplicates(title, issueDescription); + } else { + this.clearSearchResults(); + } + } + + private searchExtensionIssues(title: string): void { + const url = this.getExtensionGitHubUrl(); + if (title) { + const matches = /^https?:\/\/github\.com\/(.*)/.exec(url); + if (matches && matches.length) { + const repo = matches[1]; + return this.searchGitHub(repo, title); + } + + // If the extension has no repository, display empty search results + if (this.issueReporterModel.getData().selectedExtension) { + this.clearSearchResults(); + return this.displaySearchResults([]); + + } + } + + this.clearSearchResults(); + } + + private clearSearchResults(): void { + const similarIssues = this.getElementById('similar-issues')!; + similarIssues.innerText = ''; + this.numberOfSearchResultsDisplayed = 0; + } + + @debounce(300) + private searchGitHub(repo: string, title: string): void { + const query = `is:issue+repo:${repo}+${title}`; + const similarIssues = this.getElementById('similar-issues')!; + + window.fetch(`https://api.github.com/search/issues?q=${query}`).then((response) => { + response.json().then(result => { + similarIssues.innerText = ''; + if (result && result.items) { + this.displaySearchResults(result.items); + } else { + // If the items property isn't present, the rate limit has been hit + const message = $('div.list-title'); + message.textContent = localize('rateLimited', "GitHub query limit exceeded. Please wait."); + similarIssues.appendChild(message); + + const resetTime = response.headers.get('X-RateLimit-Reset'); + const timeToWait = resetTime ? parseInt(resetTime) - Math.floor(Date.now() / 1000) : 1; + if (this.shouldQueueSearch) { + this.shouldQueueSearch = false; + setTimeout(() => { + this.searchGitHub(repo, title); + this.shouldQueueSearch = true; + }, timeToWait * 1000); + } + } + }).catch(_ => { + // Ignore + }); + }).catch(_ => { + // Ignore + }); + } + + @debounce(300) + private searchDuplicates(title: string, body?: string): void { + const url = 'https://vscode-probot.westus.cloudapp.azure.com:7890/duplicate_candidates'; + const init = { + method: 'POST', + body: JSON.stringify({ + title, + body + }), + headers: new Headers({ + 'Content-Type': 'application/json' + }) + }; + + window.fetch(url, init).then((response) => { + response.json().then(result => { + this.clearSearchResults(); + + if (result && result.candidates) { + this.displaySearchResults(result.candidates); + } else { + throw new Error('Unexpected response, no candidates property'); + } + }).catch(_ => { + // Ignore + }); + }).catch(_ => { + // Ignore + }); + } + + private displaySearchResults(results: SearchResult[]) { + const similarIssues = this.getElementById('similar-issues')!; + if (results.length) { + const issues = $('div.issues-container'); + const issuesText = $('div.list-title'); + issuesText.textContent = localize('similarIssues', "Similar issues"); + + this.numberOfSearchResultsDisplayed = results.length < 5 ? results.length : 5; + for (let i = 0; i < this.numberOfSearchResultsDisplayed; i++) { + const issue = results[i]; + const link = $('a.issue-link', { href: issue.html_url }); + link.textContent = issue.title; + link.title = issue.title; + link.addEventListener('click', (e) => this.openLink(e)); + link.addEventListener('auxclick', (e) => this.openLink(e)); + + let issueState: HTMLElement; + let item: HTMLElement; + if (issue.state) { + issueState = $('span.issue-state'); + + const issueIcon = $('span.issue-icon'); + const codicon = new CodiconLabel(issueIcon); + codicon.text = issue.state === 'open' ? '$(issue-opened)' : '$(issue-closed)'; + + const issueStateLabel = $('span.issue-state.label'); + issueStateLabel.textContent = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed"); + + issueState.title = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed"); + issueState.appendChild(issueIcon); + issueState.appendChild(issueStateLabel); + + item = $('div.issue', undefined, issueState, link); + } else { + item = $('div.issue', undefined, link); + } + + issues.appendChild(item); + } + + similarIssues.appendChild(issuesText); + similarIssues.appendChild(issues); + } else { + const message = $('div.list-title'); + message.textContent = localize('noSimilarIssues', "No similar issues found"); + similarIssues.appendChild(message); + } + } + + private setUpTypes(): void { + 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(); + 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(); + + this.setSourceOptions(); + } + + private makeOption(value: string, description: string, disabled: boolean): HTMLOptionElement { + const option: HTMLOptionElement = document.createElement('option'); + option.disabled = disabled; + option.value = value; + option.textContent = description; + + return option; + } + + private setSourceOptions(): void { + const sourceSelect = this.getElementById('issue-source')! as HTMLSelectElement; + const { issueType, fileOnExtension, selectedExtension } = this.issueReporterModel.getData(); + let selected = sourceSelect.selectedIndex; + if (selected === -1) { + if (fileOnExtension !== undefined) { + selected = fileOnExtension ? 2 : 1; + } else if (selectedExtension?.isBuiltin) { + selected = 1; + } + } + + sourceSelect.innerText = ''; + if (issueType === IssueType.FeatureRequest) { + sourceSelect.append(...[ + this.makeOption('', localize('selectSource', "Select source"), true), + this.makeOption('false', localize('vscode', "Visual Studio Code"), false), + this.makeOption('true', localize('extension', "An extension"), false) + ]); + } else { + sourceSelect.append(...[ + this.makeOption('', localize('selectSource', "Select source"), true), + this.makeOption('false', localize('vscode', "Visual Studio Code"), false), + this.makeOption('true', localize('extension', "An extension"), false), + this.makeOption('', localize('unknown', "Don't Know"), false) + ]); + } + + if (selected !== -1 && selected < sourceSelect.options.length) { + sourceSelect.selectedIndex = selected; + } else { + sourceSelect.selectedIndex = 0; + hide(this.getElementById('problem-source-help-text')); + } + } + + private renderBlocks(): void { + // Depending on Issue Type, we render different blocks and text + const { issueType, fileOnExtension } = this.issueReporterModel.getData(); + const blockContainer = this.getElementById('block-container'); + const systemBlock = document.querySelector('.block-system'); + const processBlock = document.querySelector('.block-process'); + const workspaceBlock = document.querySelector('.block-workspace'); + const extensionsBlock = document.querySelector('.block-extensions'); + const searchedExtensionsBlock = document.querySelector('.block-searchedExtensions'); + const settingsSearchResultsBlock = document.querySelector('.block-settingsSearchResults'); + + const problemSource = this.getElementById('problem-source')!; + const descriptionTitle = this.getElementById('issue-description-label')!; + const descriptionSubtitle = this.getElementById('issue-description-subtitle')!; + const extensionSelector = this.getElementById('extension-selection')!; + + // Hide all by default + hide(blockContainer); + hide(systemBlock); + hide(processBlock); + hide(workspaceBlock); + hide(extensionsBlock); + hide(searchedExtensionsBlock); + hide(settingsSearchResultsBlock); + hide(problemSource); + hide(extensionSelector); + + if (issueType === IssueType.Bug) { + show(blockContainer); + show(systemBlock); + show(problemSource); + + if (fileOnExtension) { + show(extensionSelector); + } else { + show(extensionsBlock); + } + 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); + show(processBlock); + show(workspaceBlock); + show(problemSource); + + if (fileOnExtension) { + show(extensionSelector); + } else { + show(extensionsBlock); + } + + 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) { + 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); + } + } + } + + 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; + } + } + + private validateInputs(): boolean { + let isValid = true; + ['issue-title', 'description', 'issue-source'].forEach(elementId => { + isValid = this.validateInput(elementId) && isValid; + }); + + if (this.issueReporterModel.fileOnExtension()) { + isValid = this.validateInput('extension-selector') && isValid; + } + + return isValid; + } + + private async createIssue(): Promise { + if (!this.validateInputs()) { + // If inputs are invalid, set focus to the first one and add listeners on them + // to detect further changes + const invalidInput = document.getElementsByClassName('invalid-input'); + if (invalidInput.length) { + (invalidInput[0]).focus(); + } + + this.addEventListener('issue-title', 'input', _ => { + this.validateInput('issue-title'); + }); + + this.addEventListener('description', 'input', _ => { + this.validateInput('description'); + }); + + this.addEventListener('issue-source', 'change', _ => { + this.validateInput('issue-source'); + }); + + if (this.issueReporterModel.fileOnExtension()) { + this.addEventListener('extension-selector', 'change', _ => { + this.validateInput('extension-selector'); + }); + } + + return false; + } + + this.hasBeenSubmitted = true; + + const baseUrl = this.getIssueUrlWithTitle((this.getElementById('issue-title')).value); + const issueBody = this.issueReporterModel.serialize(); + let url = baseUrl + `&body=${encodeURIComponent(issueBody)}`; + + if (url.length > MAX_URL_LENGTH) { + try { + url = await this.writeToClipboard(baseUrl, issueBody); + } catch (_) { + return false; + } + } + + ipcRenderer.send('vscode:openExternal', url); + return true; + } + + private async writeToClipboard(baseUrl: string, issueBody: string): Promise { + return new Promise((resolve, reject) => { + ipcRenderer.once('vscode:issueReporterClipboardResponse', async (event: unknown, shouldWrite: boolean) => { + if (shouldWrite) { + await this.electronService.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(); + } + }); + + ipcRenderer.send('vscode:issueReporterClipboard'); + }); + } + + private getExtensionGitHubUrl(): string { + let repositoryUrl = ''; + const bugsUrl = this.getExtensionBugsUrl(); + const extensionUrl = this.getExtensionRepositoryUrl(); + // If given, try to match the extension's bug url + if (bugsUrl && bugsUrl.match(/^https?:\/\/github\.com\/(.*)/)) { + repositoryUrl = normalizeGitHubUrl(bugsUrl); + } else if (extensionUrl && extensionUrl.match(/^https?:\/\/github\.com\/(.*)/)) { + repositoryUrl = normalizeGitHubUrl(extensionUrl); + } + + return repositoryUrl; + } + + private getIssueUrlWithTitle(issueTitle: string): string { + let repositoryUrl = this.configuration.product.reportIssueUrl; + if (this.issueReporterModel.fileOnExtension()) { + const extensionGitHubUrl = this.getExtensionGitHubUrl(); + if (extensionGitHubUrl) { + repositoryUrl = extensionGitHubUrl + '/issues/new'; + } + } + + 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'); + + if (target) { + const systemInfo = state.systemInfo!; + 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)) { + 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 { + 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); + } + }); + } + } + + private updateExtensionSelector(extensions: IssueReporterExtensionData[]): void { + interface IOption { + name: string; + id: string; + } + + const extensionOptions: IOption[] = extensions.map(extension => { + return { + name: extension.displayName || extension.name || '', + id: extension.id + }; + }); + + // Sort extensions by name + extensionOptions.sort((a, b) => { + const aName = a.name.toLowerCase(); + const bName = b.name.toLowerCase(); + if (aName > bName) { + return 1; + } + + if (aName < bName) { + return -1; + } + + return 0; + }); + + const makeOption = (extension: IOption, selectedExtension?: IssueReporterExtensionData): HTMLOptionElement => { + const selected = selectedExtension && extension.id === selectedExtension.id; + return $('option', { + 'value': extension.id, + 'selected': selected || '' + }, extension.name); + }; + + const extensionsSelector = this.getElementById('extension-selector'); + if (extensionsSelector) { + const { selectedExtension } = this.issueReporterModel.getData(); + reset(extensionsSelector, $('option'), ...extensionOptions.map(extension => makeOption(extension, selectedExtension))); + + this.addEventListener('extension-selector', 'change', (e: Event) => { + const selectedExtensionId = (e.target).value; + const extensions = this.issueReporterModel.getData().allExtensions; + const matches = extensions.filter(extension => extension.id === selectedExtensionId); + if (matches.length) { + this.issueReporterModel.update({ selectedExtension: matches[0] }); + this.validateSelectedExtension(); + + const title = (this.getElementById('issue-title')).value; + this.searchExtensionIssues(title); + } else { + this.issueReporterModel.update({ selectedExtension: undefined }); + this.clearSearchResults(); + this.validateSelectedExtension(); + } + }); + } + + this.addEventListener('problem-source', 'change', (_) => { + this.validateSelectedExtension(); + }); + } + + private validateSelectedExtension(): void { + const extensionValidationMessage = this.getElementById('extension-selection-validation-error')!; + const extensionValidationNoUrlsMessage = this.getElementById('extension-selection-validation-error-no-url')!; + hide(extensionValidationMessage); + hide(extensionValidationNoUrlsMessage); + + if (!this.issueReporterModel.getData().selectedExtension) { + this.previewButton.enabled = true; + return; + } + + const hasValidGitHubUrl = this.getExtensionGitHubUrl(); + if (hasValidGitHubUrl) { + this.previewButton.enabled = true; + } else { + this.setExtensionValidationMessage(); + this.previewButton.enabled = false; + } + } + + private setExtensionValidationMessage(): void { + const extensionValidationMessage = this.getElementById('extension-selection-validation-error')!; + const extensionValidationNoUrlsMessage = this.getElementById('extension-selection-validation-error-no-url')!; + const bugsUrl = this.getExtensionBugsUrl(); + if (bugsUrl) { + show(extensionValidationMessage); + const link = this.getElementById('extensionBugsLink')!; + link.textContent = bugsUrl; + return; + } + + const extensionUrl = this.getExtensionRepositoryUrl(); + if (extensionUrl) { + show(extensionValidationMessage); + const link = this.getElementById('extensionBugsLink'); + link!.textContent = extensionUrl; + return; + } + + show(extensionValidationNoUrlsMessage); + } + + private updateProcessInfo(state: IssueReporterModelData) { + const target = document.querySelector('.block-process .block-info') as HTMLElement; + if (target) { + reset(target, $('code', undefined, state.processInfo)); + } + } + + private updateWorkspaceInfo(state: IssueReporterModelData) { + document.querySelector('.block-workspace .block-info code')!.textContent = '\n' + state.workspaceInfo; + } + + private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { + const target = document.querySelector('.block-extensions .block-info'); + if (target) { + if (this.configuration.disableExtensions) { + reset(target, localize('disabledExtensions', "Extensions are disabled")); + return; + } + + const themeExclusionStr = numThemeExtensions ? `\n(${numThemeExtensions} theme extensions excluded)` : ''; + extensions = extensions || []; + + if (!extensions.length) { + target.innerText = 'Extensions: none' + themeExclusionStr; + return; + } + + reset(target, this.getExtensionTableHtml(extensions), document.createTextNode(themeExclusionStr)); + } + } + + 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 { + event.preventDefault(); + event.stopPropagation(); + // Exclude right click + if (event.which < 3) { + windowOpenNoOpener((event.target).href); + } + } + + private getElementById(elementId: string): T | undefined { + const element = document.getElementById(elementId) as T | undefined; + if (element) { + return element; + } else { + return undefined; + } + } + + private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { + const element = this.getElementById(elementId); + if (element) { + element.addEventListener(eventType, handler); + } + } +} + +// helper functions + +function hide(el: Element | undefined | null) { + if (el) { + el.classList.add('hidden'); + } +} +function show(el: Element | undefined | null) { + if (el) { + el.classList.remove('hidden'); + } +} 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 6c7fa528e3770..b529e48cd76c1 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 83% rename from src/vs/code/electron-browser/issue/issueReporterPage.ts rename to src/vs/code/electron-sandbox/issue/issueReporterPage.ts index 63f42d9b960a8..d7be46aebfa29 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 => ` + @@ -32,12 +33,18 @@ export default (): string => ` + +
    + @@ -56,6 +63,7 @@ export default (): string => `
    +
    @@ -124,4 +132,4 @@ export default (): string => `
    -`; \ No newline at end of file +`; diff --git a/src/vs/code/electron-sandbox/issue/media/issueReporter.css b/src/vs/code/electron-sandbox/issue/media/issueReporter.css new file mode 100644 index 0000000000000..e3c6d8f355f8f --- /dev/null +++ b/src/vs/code/electron-sandbox/issue/media/issueReporter.css @@ -0,0 +1,374 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Table + */ + +table { + width: 100%; + max-width: 100%; + background-color: transparent; + border-collapse: collapse; +} +th { + vertical-align: bottom; + border-bottom: 1px solid; + padding: 5px; + text-align: inherit; +} +td { + padding: 5px; + vertical-align: top; +} + +tr td:first-child { + width: 30%; +} + +label { + user-select: none; +} + +.block-settingsSearchResults-details { + padding-bottom: .5rem; +} + +.block-settingsSearchResults-details > div { + padding: .5rem .75rem; +} + +.section { + margin-bottom: .5em; +} + +/** + * Forms + */ +input[type="text"], textarea { + display: block; + width: 100%; + padding: .375rem .75rem; + font-size: 1rem; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: 1px solid #ced4da; +} + +textarea { + overflow: auto; + resize: vertical; +} + +/** + * Button + */ + +.monaco-text-button { + display: block; + width: auto; + padding: 4px 10px; + align-self: flex-end; + margin-bottom: 10px; +} + +select { + height: calc(2.25rem + 2px); + display: inline-block; + padding: 3px 3px; + font-size: 14px; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: none; +} + +* { + box-sizing: border-box; +} + +textarea, input, select { + font-family: inherit; +} + +html { + color: #CCCCCC; + height: 100%; +} + +/* 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; + overflow-y: scroll; + height: 100%; +} + +.hidden { + display: none; +} + +.block { + font-size: 12px; +} + +.block .block-info { + width: 100%; + font-size: 12px; + overflow: auto; + overflow-wrap: break-word; + margin: 5px; + padding: 10px; +} + +#issue-reporter { + max-width: 85vw; + margin-left: auto; + margin-right: auto; + padding-top: 2em; + padding-bottom: 2em; + display: flex; + flex-direction: column; + height: 100%; +} + +.description-section { + flex-grow: 1; + display: flex; + flex-direction: column; + flex-shrink: 0; +} + +textarea { + flex-grow: 1; + min-height: 150px; +} + +.block-info-text { + display: flex; + flex-grow: 1; +} + +#github-submit-btn { + flex-shrink: 0; + margin-left: auto; + margin-top: 10px; + margin-bottom: 10px; +} + +.two-col { + display: inline-block; + width: 49%; +} + +#vscode-version { + width: 90%; +} + +.input-group { + margin-bottom: 1em; +} + +#extension-selection { + margin-top: 1em; +} + +select, input, textarea { + border: 1px solid transparent; + margin-top: 10px; +} + + +#issue-reporter .validation-error { + font-size: 12px; + padding: 10px; + border-top: 0px !important; +} + + +input[type="checkbox"] { + width: auto; + display: inline-block; + margin-top: 0; + vertical-align: middle; + cursor: pointer; +} + +input:disabled { + opacity: 0.6; +} + +.list-title { + margin-top: 1em; + margin-left: 1em; +} + +.instructions { + font-size: 12px; + margin-top: .5em; +} + +a, .workbenchCommand { + cursor: pointer; + border: 1px solid transparent; +} + +.workbenchCommand:disabled { + color: #868e96; + cursor: default +} + +.block-extensions .block-info { + margin-bottom: 1.5em; +} + +/* Default styles, overwritten if a theme is provided */ +input, select, textarea { + background-color: #3c3c3c; + border: none; + color: #cccccc; +} + +a { + color: #CCCCCC; + text-decoration: none; +} + +.section .input-group .validation-error { + margin-left: 100px; +} + +.section .inline-form-control, .section .inline-label { + display: inline-block; +} + +.section .inline-label { + width: 95px; +} + +.section .inline-form-control, .section .input-group .validation-error { + width: calc(100% - 100px); +} + +#issue-type { + cursor: pointer; +} + +#similar-issues { + margin-left: 15%; + display: block; +} + +#problem-source-help-text { + margin-left: calc(15% + 1em); +} + +@media (max-width: 950px) { + .section .inline-label { + width: 15%; + } + + #problem-source-help-text { + margin-left: calc(15% + 1em); + } + + .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) { + .section .inline-label { + display: none !important; + } + + #problem-source-help-text { + margin-left: 1em; + } + + .section .inline-form-control, .section .input-group .validation-error { + width: 100%; + } + + #similar-issues, .section .input-group .validation-error { + margin-left: 0; + } +} + +::-webkit-scrollbar { + width: 14px; +} + +::-webkit-scrollbar-thumb { + min-height: 20px; +} + +::-webkit-scrollbar-corner { + display: none; +} + +.issues-container { + margin-left: 1.5em; + margin-top: .5em; + max-height: 92px; + overflow-y: auto; +} + +.issues-container > .issue { + padding: 4px 0; + display: flex; +} + +.issues-container > .issue > .issue-link { + width: calc(100% - 82px); + overflow: hidden; + padding-top: 3px; + white-space: nowrap; + text-overflow: ellipsis; +} + +.issues-container > .issue > .issue-state .codicon { + width: 16px; +} + +.issues-container > .issue > .issue-state { + width: 77px; + padding: 3px 6px; + margin-right: 5px; + color: #CCCCCC; + background-color: #3c3c3c; + border-radius: .25rem; +} + +.issues-container > .issue .label { + margin-left: 5px; + width: 44px; + text-overflow: ellipsis; + overflow: hidden; +} 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 b34917961c05b..b09f4fb760f4e 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 0000000000000..add9cdae262e2 --- /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 0000000000000..517805abdb786 --- /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 0000000000000..d6c6886e6fd82 --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +/** + * @type {{ load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown }} + */ +const bootstrapWindow = (() => { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; +})(); + +bootstrapWindow.load(['vs/code/electron-sandbox/processExplorer/processExplorerMain'], function (processExplorer, configuration) { + processExplorer.startup(configuration.windowId, configuration.data); +}, { forceEnableDeveloperKeybindings: true }); 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 0000000000000..6223277bc0b8c --- /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 { ElectronService, IElectronService } from 'vs/platform/electron/electron-sandbox/electron'; +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, addClass, $ } 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'; + +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 electronService: IElectronService; + + constructor(windowId: number, private data: ProcessExplorerData) { + const mainProcessService = new MainProcessService(windowId); + this.electronService = new ElectronService(windowId, mainProcessService) as IElectronService; + + 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); + + const MB = 1024 * 1024; + + 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 / 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.electronService.getTotalMem(); + 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.innerHTML = 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.electronService.killProcess(pid, 'SIGTERM'); + } + }); + + items.push({ + label: localize('forceKillProcess', "Force Kill Process"), + click: () => { + this.electronService.killProcess(pid, 'SIGKILL'); + } + }); + + items.push({ + type: 'separator' + }); + } + + items.push({ + label: localize('copy', "Copy"), + click: () => { + const row = document.getElementById(pid.toString()); + if (row) { + this.electronService.writeClipboardText(row.innerText); + } + } + }); + + items.push({ + label: localize('copyAll', "Copy All"), + click: () => { + const processList = document.getElementById('process-list'); + if (processList) { + this.electronService.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'; + addClass(document.body, 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 + zooms in + if (cmdOrCtrlKey && e.keyCode === 187) { + zoomIn(); + } + + // Cmd/Ctrl - zooms out + if (cmdOrCtrlKey && e.keyCode === 189) { + zoomOut(); + } + }; + + // Cmd/Ctrl + w closes process explorer + window.addEventListener('keydown', e => { + const cmdOrCtrlKey = data.platform === 'darwin' ? e.metaKey : e.ctrlKey; + if (cmdOrCtrlKey && e.keyCode === 87) { + processExplorer.dispose(); + ipcRenderer.send('vscode:closeProcessExplorer'); + } + }); +} diff --git a/src/vs/code/electron-sandbox/proxy/auth.html b/src/vs/code/electron-sandbox/proxy/auth.html new file mode 100644 index 0000000000000..788b68fce7281 --- /dev/null +++ b/src/vs/code/electron-sandbox/proxy/auth.html @@ -0,0 +1,83 @@ + + + + + + + + + + + +

    +
    +

    +
    +

    +

    +

    + + +

    +
    +
    + + + + + 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 0000000000000..5e0db3c2dc711 --- /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 0000000000000..40737461d2930 --- /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 0000000000000..bac5dd6d6e81c --- /dev/null +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * 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 perf = (function () { + globalThis.MonacoPerformanceMarks = globalThis.MonacoPerformanceMarks || []; + return { + /** + * @param {string} name + */ + mark(name) { + globalThis.MonacoPerformanceMarks.push(name, Date.now()); + } + }; +})(); + +perf.mark('renderer/started'); + +/** + * @type {{ + * load: (modules: string[], resultCallback: (result, configuration: object) => any, options: object) => unknown, + * globals: () => typeof import('../../../base/parts/sandbox/electron-sandbox/globals') + * }} + */ +const bootstrapWindow = (() => { + // @ts-ignore (defined in bootstrap-window.js) + return window.MonacoBootstrapWindow; +})(); + +// Load environment in parallel to workbench loading to avoid waterfall +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'); + } + } +); diff --git a/src/vs/code/node/activeWindowTracker.ts b/src/vs/code/node/activeWindowTracker.ts deleted file mode 100644 index a7dbeb98bf2fd..0000000000000 --- a/src/vs/code/node/activeWindowTracker.ts +++ /dev/null @@ -1,51 +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 { DisposableStore, Disposable } from 'vs/base/common/lifecycle'; -import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; -import { IElectronService } from 'vs/platform/electron/node/electron'; - -export class ActiveWindowManager extends Disposable { - - private readonly disposables = this._register(new DisposableStore()); - private firstActiveWindowIdPromise: CancelablePromise | undefined; - - private activeWindowId: number | undefined; - - constructor(@IElectronService electronService: IElectronService) { - super(); - - // remember last active window id upon events - const onActiveWindowChange = Event.latch(Event.any(electronService.onWindowOpen, electronService.onWindowFocus)); - onActiveWindowChange(this.setActiveWindow, this, this.disposables); - - // resolve current active window - this.firstActiveWindowIdPromise = createCancelablePromise(() => electronService.getActiveWindowId()); - (async () => { - try { - const windowId = await this.firstActiveWindowIdPromise; - this.activeWindowId = (typeof this.activeWindowId === 'number') ? this.activeWindowId : windowId; - } finally { - this.firstActiveWindowIdPromise = undefined; - } - })(); - } - - private setActiveWindow(windowId: number | undefined) { - if (this.firstActiveWindowIdPromise) { - this.firstActiveWindowIdPromise.cancel(); - this.firstActiveWindowIdPromise = undefined; - } - - this.activeWindowId = windowId; - } - - async getActiveClientId(): Promise { - const id = this.firstActiveWindowIdPromise ? (await this.firstActiveWindowIdPromise) : this.activeWindowId; - - return `window:${id}`; - } -} diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 5a6c573b36db0..1cdfe65520fab 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -7,19 +7,19 @@ 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 { resolveTerminalEncoding } from 'vs/base/node/encoding'; 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'] @@ -29,11 +29,11 @@ function shouldSpawnCliProcess(argv: ParsedArgs): boolean { } 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 +44,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)); } @@ -128,7 +128,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,95 +138,59 @@ 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(c => child.once('exit', () => c())); }); } - let stdinWithoutTty: boolean = false; - try { - stdinWithoutTty = !process.stdin.isTTY; // Via https://twitter.com/MylesBorins/status/782009479382626304 - } catch (error) { - // Windows workaround for https://github.com/nodejs/node/issues/11656 - } - - const readFromStdin = args._.some(a => a === '-'); - if (readFromStdin) { + const hasReadStdinArg = args._.some(a => a === '-'); + if (hasReadStdinArg) { // remove the "-" argument when we read from stdin args._ = args._.filter(a => a !== '-'); argv = argv.filter(a => a !== '-'); } - let stdinFilePath: string; - if (stdinWithoutTty) { + let stdinFilePath: string | undefined; + if (hasStdinWithoutTty()) { // 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) - if (args._.length === 0 && readFromStdin) { - - // prepare temp file to read stdin to - stdinFilePath = paths.join(os.tmpdir(), `code-stdin-${Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 3)}.txt`); - // open tmp file for writing - let stdinFileError: Error | undefined; - let stdinFileStream: fs.WriteStream; - try { - stdinFileStream = fs.createWriteStream(stdinFilePath); - } catch (error) { - stdinFileError = error; - } + if (args._.length === 0) { + if (hasReadStdinArg) { + stdinFilePath = getStdinFilePath(); - if (!stdinFileError) { + // returns a file path where stdin input is written into (write in progress). + try { + readFromStdin(stdinFilePath, !!verbose); // throws error if file can not be written - // Pipe into tmp file using terminals encoding - resolveTerminalEncoding(verbose).then(async encoding => { - const iconv = await import('iconv-lite'); - const converterStream = iconv.decodeStream(encoding); - process.stdin.pipe(converterStream).pipe(stdinFileStream); - }); + // Make sure to open tmp file + addArg(argv, stdinFilePath); - // Make sure to open tmp file - addArg(argv, stdinFilePath); - - // Enable --wait to get all data and ignore adding this to history - addArg(argv, '--wait'); - addArg(argv, '--skip-add-to-recently-opened'); - args.wait = true; - } + // Enable --wait to get all data and ignore adding this to history + addArg(argv, '--wait'); + addArg(argv, '--skip-add-to-recently-opened'); + args.wait = true; - if (verbose) { - if (stdinFileError) { - console.error(`Failed to create file to read via stdin: ${stdinFileError.toString()}`); - } else { console.log(`Reading from stdin via: ${stdinFilePath}`); + } catch (e) { + console.log(`Failed to create file to read via stdin: ${e.toString()}`); + stdinFilePath = undefined; } - } - } - - // If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message - // if we detect that data flows into via stdin after a certain timeout. - else if (args._.length === 0) { - processCallbacks.push(child => new Promise(c => { - const dataListener = () => { - if (isWindows) { - console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`); - } else { - console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`); + } else { + + // If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message + // if we detect that data flows into via stdin after a certain timeout. + processCallbacks.push(_ => stdinDataListener(1000).then(dataReceived => { + if (dataReceived) { + if (isWindows) { + console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`); + } else { + console.log(`Run with '${product.applicationName} -' to read from stdin (e.g. 'ps aux | grep code | ${product.applicationName} -').`); + } } - - c(undefined); - }; - - // wait for 1s maximum... - setTimeout(() => { - process.stdin.removeListener('data', dataListener); - - c(undefined); - }, 1000); - - // ...but finish early if we detect data - process.stdin.once('data', dataListener); - })); + })); + } } } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 2ba0de50620d2..a3928092c232f 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; +import { raceTimeout } from 'vs/base/common/async'; 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 { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IExtensionManagementService, IExtensionGalleryService, IGalleryExtension, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; @@ -24,7 +24,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 +35,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 +49,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) { @@ -74,18 +74,18 @@ export class Main { constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService 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']); + await this.installExtensions(argv['install-extension'], !!argv['force'], !!argv['do-not-sync']); } else if (argv['uninstall-extension']) { await this.uninstallExtension(argv['uninstall-extension']); } else if (argv['locate-extension']) { @@ -101,8 +101,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,7 +124,7 @@ 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[], force: boolean, doNotSync: boolean): Promise { const failed: string[] = []; const installedExtensionsManifests: IExtensionManifest[] = []; if (extensions.length) { @@ -134,7 +133,7 @@ export class Main { for (const extension of extensions) { try { - const manifest = await this.installExtension(extension, force); + const manifest = await this.installExtension(extension, force, doNotSync); if (manifest) { installedExtensionsManifests.push(manifest); } @@ -149,7 +148,7 @@ export class Main { return failed.length ? Promise.reject(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))) : Promise.resolve(); } - private async installExtension(extension: string, force: boolean): Promise { + private async installExtension(extension: string, force: boolean, doNotSync: boolean): Promise { if (/\.vsix$/i.test(extension)) { extension = path.isAbsolute(extension) ? extension : path.join(process.cwd(), extension); @@ -157,7 +156,7 @@ export class Main { const valid = await this.validate(manifest, force); if (valid) { - return this.extensionManagementService.install(URI.file(extension)).then(id => { + return this.extensionManagementService.install(URI.file(extension), doNotSync).then(id => { console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed.", getBaseLabel(extension))); return manifest; }, error => { @@ -204,7 +203,7 @@ export class Main { } console.log(localize('updateMessage', "Updating the extension '{0}' to the version {1}", id, extension.version)); } - await this.installFromGallery(id, extension); + await this.installFromGallery(id, extension, doNotSync); return manifest; })); } @@ -214,9 +213,11 @@ export class Main { throw new Error('Invalid vsix'); } + const semver = await import('semver-umd'); + 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,11 +227,11 @@ export class Main { return true; } - private async installFromGallery(id: string, extension: IGalleryExtension): Promise { + private async installFromGallery(id: string, extension: IGalleryExtension, doNotSync: boolean): Promise { console.log(localize('installing', "Installing extension '{0}' v{1}...", id, extension.version)); try { - await this.extensionManagementService.installFromGallery(extension); + await this.extensionManagementService.installFromGallery(extension, doNotSync); console.log(localize('successInstall', "Extension '{0}' v{1} was successfully installed.", id, extension.version)); } catch (error) { if (isPromiseCanceledError(error)) { @@ -294,11 +295,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 EnvironmentService(argv); const logService: ILogService = new SpdLogService('cli', environmentService.logsPath, getLogLevel(environmentService)); process.once('exit', () => logService.dispose()); logService.info('main', argv); @@ -306,16 +307,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 +316,37 @@ 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(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)); } 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 +362,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 f605a4526f35e..0000000000000 --- 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 b431b6d05140a..a089ffe4f8448 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 c8ffd0c8dbc14..1ce46448038d4 100644 --- a/src/vs/code/test/electron-main/nativeHelpers.test.ts +++ b/src/vs/code/test/electron-main/nativeHelpers.test.ts @@ -28,6 +28,7 @@ suite('Windows Native Helpers', () => { }); test('vscode-windows-ca-certs', async () => { + // @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/code/test/electron-main/windowsStateStorage.test.ts b/src/vs/code/test/electron-main/windowsStateStorage.test.ts index 6094456c6f59c..419caee0d71af 100644 --- a/src/vs/code/test/electron-main/windowsStateStorage.test.ts +++ b/src/vs/code/test/electron-main/windowsStateStorage.test.ts @@ -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. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import * as os from 'os'; import * as path from 'vs/base/common/path'; diff --git a/src/vs/css.build.js b/src/vs/css.build.js index 69c6240891d35..11041a226286f 100644 --- a/src/vs/css.build.js +++ b/src/vs/css.build.js @@ -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 4a2d5a4d29972..129fd29abd945 100644 --- a/src/vs/css.js +++ b/src/vs/css.js @@ -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]); } diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts index 21a546634121b..97fe48dd73291 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 471c97e1162a9..ee76fd4b9032c 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 { @@ -87,6 +88,8 @@ export interface ISerializedFontInfo { readonly typicalFullwidthCharacterWidth: number; readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; + middotWidth: number; + wsmiddotWidth: number; readonly maxDigitWidth: number; } @@ -159,6 +162,8 @@ class CSSBasedConfiguration extends Disposable { const savedFontInfo = savedFontInfos[i]; // 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); } @@ -183,6 +188,8 @@ class CSSBasedConfiguration extends Disposable { typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5), 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); } @@ -223,7 +230,11 @@ class CSSBasedConfiguration extends Disposable { const rightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, monospace); const halfwidthRightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, null); - this.createRequest('·', CharWidthRequestType.Regular, all, monospace); + // 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); @@ -289,6 +300,8 @@ class CSSBasedConfiguration extends Disposable { typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width, canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow, spaceWidth: space.width, + middotWidth: middot.width, + wsmiddotWidth: wsmiddotWidth.width, maxDigitWidth: maxDigitWidth }, canTrustBrowserZoomLevel); } @@ -333,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(); } @@ -374,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 16eea46d1f303..06b42c6f9c6da 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 94ed9e159cbce..63e9488203a1a 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -7,10 +7,10 @@ import * as nls from 'vs/nls'; 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 +20,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_ { @@ -274,6 +275,48 @@ 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()) { + this.runEditorCommand(accessor, focusedEditor, args); + return true; + } + 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(); + this.runEditorCommand(accessor, activeEditor, args); + return true; + } + return false; + }); + } + + public abstract runDOMCommand(): void; + public abstract runEditorCommand(accessor: ServicesAccessor | null, editor: ICodeEditor, args: any): void; +} + export namespace CoreNavigationCommands { class BaseMoveToCommand extends CoreEditorCommand { @@ -285,16 +328,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 +354,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 +384,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 +410,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 +429,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 +443,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 +481,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 +519,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 +573,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 +588,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 +812,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 +841,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 +854,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 +871,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 +896,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 +931,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 +994,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 +1010,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 +1039,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 +1127,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 +1171,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 +1215,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 +1256,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 +1292,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 +1318,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 +1343,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 +1369,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 +1389,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 +1422,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 +1447,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 +1480,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 +1521,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 +1547,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 +1574,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 +1596,20 @@ 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; 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 +1629,38 @@ 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 runCoreEditorCommand(cursors: ICursors, args: any): void { - cursors.context.model.pushStackElement(); - cursors.setStates( - args.source, + public runDOMCommand(): void { + 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 +1670,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 +1683,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 +1737,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 +1759,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 +1782,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 +1793,7 @@ export namespace CoreEditingCommands { constructor() { super({ id: 'deleteLeft', - precondition: EditorContextKeys.writable, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1625,13 +1804,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 +1818,7 @@ export namespace CoreEditingCommands { constructor() { super({ id: 'deleteRight', - precondition: EditorContextKeys.writable, + precondition: undefined, kbOpts: { weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, @@ -1649,72 +1828,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 { + if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) { + 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 { + if (!editor.hasModel() || editor.getOption(EditorOption.readOnly) === true) { + return; + } + editor.getModel().redo(); + } + }(); } /** @@ -1743,64 +1895,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 - }, - menubarOpts: { - 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 - }, - menubarOpts: { - 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 } - }, - menubarOpts: { - 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 d1a1984102862..1e7b0929da405 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -3,17 +3,15 @@ * 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 } from 'vs/editor/browser/controller/mouseTarget'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { HitTestContext, IViewZoneData, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; +import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ClientCoordinates, EditorMouseEvent, EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, createEditorPagePosition } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; -import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; @@ -26,8 +24,8 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; /** * Merges mouse events when mouse move events are throttled */ -function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) { - return function (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent { +export function createMouseMoveEventMerger(mouseTargetFactory: MouseTargetFactory | null) { + return function (lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent { let targetIsWidget = false; if (mouseTargetFactory) { targetIsWidget = mouseTargetFactory.mouseTargetIsWidget(currentEvent); @@ -46,9 +44,9 @@ export interface IPointerHandlerHelper { focusTextArea(): void; /** - * Get the last rendered information of the cursors. + * Get the last rendered information for cursors & textarea. */ - getLastViewCursorsRenderData(): IViewCursorRenderData[]; + getLastRenderData(): PointerHandlerLastRenderData; shouldSuppressMouseDownOnViewZone(viewZoneId: string): boolean; shouldSuppressMouseDownOnWidget(widgetId: string): boolean; @@ -70,9 +68,7 @@ export class MouseHandler extends ViewEventHandler { protected viewController: ViewController; protected viewHelper: IPointerHandlerHelper; protected mouseTargetFactory: MouseTargetFactory; - private readonly _asyncFocus: RunOnceScheduler; - - private readonly _mouseDownOperation: MouseDownOperation; + protected readonly _mouseDownOperation: MouseDownOperation; private lastMouseLeaveTime: number; constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { @@ -91,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); @@ -124,7 +118,7 @@ export class MouseHandler extends ViewEventHandler { e.stopPropagation(); } }; - this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, browser.isEdgeOrIE ? 'mousewheel' : 'wheel', onMouseWheel, true)); + this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { capture: true, passive: false })); this._context.addEventHandler(this); } @@ -139,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 { @@ -150,7 +142,7 @@ export class MouseHandler extends ViewEventHandler { } // --- end event handlers - public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null { + public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null { const clientPos = new ClientCoordinates(clientX, clientY); const pos = clientPos.toPageCoordinates(); const editorPos = createEditorPagePosition(this.viewHelper.viewDomNode); @@ -159,13 +151,11 @@ export class MouseHandler extends ViewEventHandler { return null; } - const lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData(); - return this.mouseTargetFactory.createMouseTarget(lastViewCursorsRenderData, editorPos, pos, null); + return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), editorPos, pos, null); } - protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): editorBrowser.IMouseTarget { - const lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData(); - return this.mouseTargetFactory.createMouseTarget(lastViewCursorsRenderData, e.editorPos, e.pos, testEventTarget ? e.target : null); + protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): IMouseTarget { + return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, testEventTarget ? e.target : null); } private _getMouseColumn(e: EditorMouseEvent): number { @@ -179,7 +169,7 @@ export class MouseHandler extends ViewEventHandler { }); } - private _onMouseMove(e: EditorMouseEvent): void { + public _onMouseMove(e: EditorMouseEvent): void { if (this._mouseDownOperation.isActive()) { // In selection/drag operation return; @@ -196,7 +186,7 @@ export class MouseHandler extends ViewEventHandler { }); } - private _onMouseLeave(e: EditorMouseEvent): void { + public _onMouseLeave(e: EditorMouseEvent): void { this.lastMouseLeaveTime = (new Date()).getTime(); this.viewController.emitMouseLeave({ event: e, @@ -214,12 +204,12 @@ export class MouseHandler extends ViewEventHandler { public _onMouseDown(e: EditorMouseEvent): void { const t = this._createMouseTarget(e, true); - const targetIsContent = (t.type === editorBrowser.MouseTargetType.CONTENT_TEXT || t.type === editorBrowser.MouseTargetType.CONTENT_EMPTY); - const targetIsGutter = (t.type === editorBrowser.MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS || t.type === editorBrowser.MouseTargetType.GUTTER_LINE_DECORATIONS); - const targetIsLineNumbers = (t.type === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); + const targetIsContent = (t.type === MouseTargetType.CONTENT_TEXT || t.type === MouseTargetType.CONTENT_EMPTY); + const targetIsGutter = (t.type === MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === MouseTargetType.GUTTER_LINE_NUMBERS || t.type === MouseTargetType.GUTTER_LINE_DECORATIONS); + const targetIsLineNumbers = (t.type === MouseTargetType.GUTTER_LINE_NUMBERS); const selectOnLineNumbers = this._context.configuration.options.get(EditorOption.selectOnLineNumbers); - const targetIsViewZone = (t.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE); - const targetIsWidget = (t.type === editorBrowser.MouseTargetType.CONTENT_WIDGET); + const targetIsViewZone = (t.type === MouseTargetType.CONTENT_VIEW_ZONE || t.type === MouseTargetType.GUTTER_VIEW_ZONE); + const targetIsWidget = (t.type === MouseTargetType.CONTENT_WIDGET); let shouldHandle = e.leftButton || e.middleButton; if (platform.isMacintosh && e.leftButton && e.ctrlKey) { @@ -227,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))) { @@ -273,7 +256,7 @@ class MouseDownOperation extends Disposable { private readonly _context: ViewContext; private readonly _viewController: ViewController; private readonly _viewHelper: IPointerHandlerHelper; - private readonly _createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => editorBrowser.IMouseTarget; + private readonly _createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => IMouseTarget; private readonly _getMouseColumn: (e: EditorMouseEvent) => number; private readonly _mouseMoveMonitor: GlobalEditorMouseMoveMonitor; @@ -288,7 +271,7 @@ class MouseDownOperation extends Disposable { context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper, - createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => editorBrowser.IMouseTarget, + createMouseTarget: (e: EditorMouseEvent, testEventTarget: boolean) => IMouseTarget, getMouseColumn: (e: EditorMouseEvent) => number ) { super(); @@ -335,10 +318,10 @@ class MouseDownOperation extends Disposable { } } - public start(targetType: editorBrowser.MouseTargetType, e: EditorMouseEvent): void { + public start(targetType: MouseTargetType, e: EditorMouseEvent): void { this._lastMouseEvent = e; - this._mouseState.setStartedOnLineNumbers(targetType === editorBrowser.MouseTargetType.GUTTER_LINE_NUMBERS); + this._mouseState.setStartedOnLineNumbers(targetType === MouseTargetType.GUTTER_LINE_NUMBERS); this._mouseState.setStartButtons(e); this._mouseState.setModifiers(e); const position = this._findMousePosition(e, true); @@ -356,17 +339,20 @@ 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 && !this._currentSelection.isEmpty() // we don't drag single cursor - && (position.type === editorBrowser.MouseTargetType.CONTENT_TEXT) // single click on text + && (position.type === MouseTargetType.CONTENT_TEXT) // single click on text && position.position && this._currentSelection.containsPosition(position.position) // single click on a selection ) { this._mouseState.isDragAndDrop = true; this._isActive = true; this._mouseMoveMonitor.startMonitoring( + e.target, + e.buttons, createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), () => { @@ -390,6 +376,8 @@ class MouseDownOperation extends Disposable { if (!this._isActive) { this._isActive = true; this._mouseMoveMonitor.startMonitoring( + e.target, + e.buttons, createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), () => this._stop() @@ -440,12 +428,12 @@ class MouseDownOperation extends Disposable { if (viewZoneData) { const newPosition = this._helpPositionJumpOverViewZone(viewZoneData); if (newPosition) { - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); } } const aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(aboveLineNumber, 1)); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(aboveLineNumber, 1)); } if (e.posy > editorContent.y + editorContent.height) { @@ -454,22 +442,22 @@ class MouseDownOperation extends Disposable { if (viewZoneData) { const newPosition = this._helpPositionJumpOverViewZone(viewZoneData); if (newPosition) { - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); } } const belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); } const possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + (e.posy - editorContent.y)); if (e.posx < editorContent.x) { - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, 1)); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, 1)); } if (e.posx > editorContent.x + editorContent.width) { - return new MouseTarget(null, editorBrowser.MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); + return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); } return null; @@ -487,7 +475,7 @@ class MouseDownOperation extends Disposable { return null; } - if (t.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || t.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE) { + if (t.type === MouseTargetType.CONTENT_VIEW_ZONE || t.type === MouseTargetType.GUTTER_VIEW_ZONE) { const newPosition = this._helpPositionJumpOverViewZone(t.detail); if (newPosition) { return new MouseTarget(t.element, t.type, t.mouseColumn, newPosition, null, t.detail); diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 1f7377742484a..6123c7971ae9c 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -17,6 +17,7 @@ import { HorizontalPosition } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; +import * as dom from 'vs/base/browser/dom'; export interface IViewZoneData { viewZoneId: string; @@ -79,7 +80,7 @@ interface IETextRange { setEndPoint(how: string, SourceRange: IETextRange): void; } -declare var IETextRange: { +declare const IETextRange: { prototype: IETextRange; new(): IETextRange; }; @@ -89,6 +90,13 @@ interface IHitTestResult { hitTarget: Element | null; } +export class PointerHandlerLastRenderData { + constructor( + public readonly lastViewCursorsRenderData: IViewCursorRenderData[], + public readonly lastTextareaPosition: Position | null + ) { } +} + export class MouseTarget implements IMouseTarget { public readonly element: Element | null; @@ -232,19 +240,19 @@ export class HitTestContext { public readonly viewDomNode: HTMLElement; public readonly lineHeight: number; public readonly typicalHalfwidthCharacterWidth: number; - public readonly lastViewCursorsRenderData: IViewCursorRenderData[]; + public readonly lastRenderData: PointerHandlerLastRenderData; private readonly _context: ViewContext; private readonly _viewHelper: IPointerHandlerHelper; - constructor(context: ViewContext, viewHelper: IPointerHandlerHelper, lastViewCursorsRenderData: IViewCursorRenderData[]) { + constructor(context: ViewContext, viewHelper: IPointerHandlerHelper, lastRenderData: PointerHandlerLastRenderData) { this.model = context.model; const options = context.configuration.options; this.layoutInfo = options.get(EditorOption.layoutInfo); this.viewDomNode = viewHelper.viewDomNode; this.lineHeight = options.get(EditorOption.lineHeight); this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; - this.lastViewCursorsRenderData = lastViewCursorsRenderData; + this.lastRenderData = lastRenderData; this._context = context; this._viewHelper = viewHelper; } @@ -412,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); } @@ -462,8 +470,8 @@ export class MouseTargetFactory { return false; } - public createMouseTarget(lastViewCursorsRenderData: IViewCursorRenderData[], editorPos: EditorPagePosition, pos: PageCoordinates, target: HTMLElement | null): IMouseTarget { - const ctx = new HitTestContext(this._context, this._viewHelper, lastViewCursorsRenderData); + public createMouseTarget(lastRenderData: PointerHandlerLastRenderData, editorPos: EditorPagePosition, pos: PageCoordinates, target: HTMLElement | null): IMouseTarget { + const ctx = new HitTestContext(this._context, this._viewHelper, lastRenderData); const request = new HitTestRequest(ctx, editorPos, pos, target); try { const r = MouseTargetFactory._createMouseTarget(ctx, request, false); @@ -544,7 +552,7 @@ export class MouseTargetFactory { if (request.target) { // Check if we've hit a painted cursor - const lastViewCursorsRenderData = ctx.lastViewCursorsRenderData; + const lastViewCursorsRenderData = ctx.lastRenderData.lastViewCursorsRenderData; for (const d of lastViewCursorsRenderData) { @@ -560,7 +568,7 @@ export class MouseTargetFactory { // first or last rendered view line dom node, therefore help it out // and first check if we are on top of a cursor - const lastViewCursorsRenderData = ctx.lastViewCursorsRenderData; + const lastViewCursorsRenderData = ctx.lastRenderData.lastViewCursorsRenderData; const mouseContentHorizontalOffset = request.mouseContentHorizontalOffset; const mouseVerticalOffset = request.mouseVerticalOffset; @@ -602,7 +610,10 @@ export class MouseTargetFactory { private static _hitTestTextArea(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { // Is it the textarea? if (ElementPath.isTextArea(request.targetPath)) { - return request.fulfill(MouseTargetType.TEXTAREA); + if (ctx.lastRenderData.lastTextareaPosition) { + return request.fulfill(MouseTargetType.CONTENT_TEXT, ctx.lastRenderData.lastTextareaPosition); + } + return request.fulfill(MouseTargetType.TEXTAREA, ctx.lastRenderData.lastTextareaPosition); } return null; } @@ -663,6 +674,13 @@ export class MouseTargetFactory { const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineNumber, 1), undefined, detail); } + + const lineWidth = ctx.getLineWidth(lineNumber); + if (request.mouseContentHorizontalOffset >= lineWidth) { + const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); + const pos = new Position(lineNumber, ctx.model.getLineMaxColumn(lineNumber)); + return request.fulfill(MouseTargetType.CONTENT_EMPTY, pos, undefined, detail); + } } // We have already executed hit test... @@ -818,8 +836,17 @@ export class MouseTargetFactory { } private static _actualDoHitTestWithCaretRangeFromPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult { - - const range: Range = document.caretRangeFromPoint(coords.clientX, coords.clientY); + const shadowRoot = dom.getShadowRoot(ctx.viewDomNode); + let range: Range; + if (shadowRoot) { + if (typeof shadowRoot.caretRangeFromPoint === 'undefined') { + range = shadowCaretRangeFromPoint(shadowRoot, coords.clientX, coords.clientY); + } else { + range = shadowRoot.caretRangeFromPoint(coords.clientX, coords.clientY); + } + } else { + range = document.caretRangeFromPoint(coords.clientX, coords.clientY); + } if (!range || !range.startContainer) { return { @@ -898,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 @@ -992,3 +1036,94 @@ export class MouseTargetFactory { }; } } + +export function shadowCaretRangeFromPoint(shadowRoot: ShadowRoot, x: number, y: number): Range { + const range = document.createRange(); + + // Get the element under the point + let el: Element | null = shadowRoot.elementFromPoint(x, y); + + if (el !== null) { + // Get the last child of the element until its firstChild is a text node + // This assumes that the pointer is on the right of the line, out of the tokens + // and that we want to get the offset of the last token of the line + while (el && el.firstChild && el.firstChild.nodeType !== el.firstChild.TEXT_NODE) { + el = el.lastChild; + } + + // Grab its rect + const rect = el.getBoundingClientRect(); + + // And its font + const font = window.getComputedStyle(el, null).getPropertyValue('font'); + + // And also its txt content + const text = (el as any).innerText; + + // Position the pixel cursor at the left of the element + let pixelCursor = rect.left; + let offset = 0; + let step: number; + + // If the point is on the right of the box put the cursor after the last character + if (x > rect.left + rect.width) { + offset = text.length; + } else { + const charWidthReader = CharWidthReader.getInstance(); + // Goes through all the characters of the innerText, and checks if the x of the point + // belongs to the character. + for (let i = 0; i < text.length + 1; i++) { + // The step is half the width of the character + step = charWidthReader.getCharWidth(text.charAt(i), font) / 2; + // Move to the center of the character + pixelCursor += step; + // If the x of the point is smaller that the position of the cursor, the point is over that character + if (x < pixelCursor) { + offset = i; + break; + } + // Move between the current character and the next + pixelCursor += step; + } + } + + // Creates a range with the text node of the element and set the offset found + range.setStart(el.firstChild!, offset); + range.setEnd(el.firstChild!, offset); + } + + return range; +} + +class CharWidthReader { + private static _INSTANCE: CharWidthReader | null = null; + + public static getInstance(): CharWidthReader { + if (!CharWidthReader._INSTANCE) { + CharWidthReader._INSTANCE = new CharWidthReader(); + } + return CharWidthReader._INSTANCE; + } + + private readonly _cache: { [cacheKey: string]: number; }; + private readonly _canvas: HTMLCanvasElement; + + private constructor() { + this._cache = {}; + this._canvas = document.createElement('canvas'); + } + + public getCharWidth(char: string, font: string): number { + const cacheKey = char + font; + if (this._cache[cacheKey]) { + return this._cache[cacheKey]; + } + + const context = this._canvas.getContext('2d')!; + context.font = font; + const metrics = context.measureText(char); + const width = metrics.width; + this._cache[cacheKey] = width; + return width; + } +} diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 51988d09968cd..83fd67d6fd830 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -4,20 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import * as platform from 'vs/base/common/platform'; import { EventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { IPointerHandlerHelper, MouseHandler } from 'vs/editor/browser/controller/mouseHandler'; +import { IPointerHandlerHelper, MouseHandler, createMouseMoveEventMerger } from 'vs/editor/browser/controller/mouseHandler'; import { IMouseTarget } from 'vs/editor/browser/editorBrowser'; -import { EditorMouseEvent } from 'vs/editor/browser/editorDom'; +import { EditorMouseEvent, EditorPointerEventFactory } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; interface IThrottledGestureEvent { translationX: number; translationY: number; } -function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent, currentEvent: MSGestureEvent): IThrottledGestureEvent { +function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent | null, currentEvent: MSGestureEvent): IThrottledGestureEvent { const r = { translationY: currentEvent.translationY, translationX: currentEvent.translationX @@ -30,9 +32,9 @@ function gestureChangeEventMerger(lastEvent: IThrottledGestureEvent, currentEven } /** - * Basically IE10 and IE11 + * Basically Edge but should be modified to handle any pointerEnabled, even without support of MSGesture */ -class MsPointerHandler extends MouseHandler implements IDisposable { +class StandardPointerHandler extends MouseHandler implements IDisposable { private _lastPointerType: string; private _installGestureHandlerTimeout: number; @@ -40,25 +42,25 @@ class MsPointerHandler extends MouseHandler implements IDisposable { constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { super(context, viewController, viewHelper); - this.viewHelper.linesContentDomNode.style.msTouchAction = 'none'; - this.viewHelper.linesContentDomNode.style.msContentZooming = 'none'; + this.viewHelper.linesContentDomNode.style.touchAction = '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) { + + // TODO@Alex: replace the usage of MSGesture here with something that works across all browsers + 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 + this.viewHelper.linesContentDomNode.addEventListener('pointerdown', (e: PointerEvent) => { const pointerType = e.pointerType; - if (pointerType === ((e).MSPOINTER_TYPE_MOUSE || 'mouse')) { + if (pointerType === 'mouse') { this._lastPointerType = 'mouse'; return; - } else if (pointerType === ((e).MSPOINTER_TYPE_TOUCH || 'touch')) { + } else if (pointerType === 'touch') { this._lastPointerType = 'touch'; touchGesture.addPointer(e.pointerId); } else { @@ -66,7 +68,7 @@ class MsPointerHandler extends MouseHandler implements IDisposable { penGesture.addPointer(e.pointerId); } }); - this._register(dom.addDisposableThrottledListener(this.viewHelper.linesContentDomNode, 'MSGestureChange', (e) => this._onGestureChange(e), gestureChangeEventMerger)); + 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); @@ -98,7 +100,7 @@ class MsPointerHandler 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 { @@ -108,80 +110,83 @@ class MsPointerHandler extends MouseHandler implements IDisposable { } /** - * Basically Edge but should be modified to handle any pointerEnabled, even without support of MSGesture + * Currently only tested on iOS 13/ iPadOS. */ -class StandardPointerHandler extends MouseHandler implements IDisposable { - +export class PointerEventHandler extends MouseHandler { private _lastPointerType: string; - private _installGestureHandlerTimeout: number; - constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) { super(context, viewController, viewHelper); - this.viewHelper.linesContentDomNode.style.touchAction = 'none'; + this._register(Gesture.addTarget(this.viewHelper.linesContentDomNode)); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Tap, (e) => this.onTap(e))); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Change, (e) => this.onChange(e))); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, EventType.Contextmenu, (e: MouseEvent) => this._onContextMenu(new EditorMouseEvent(e, this.viewHelper.viewDomNode), false))); - // 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; + this._lastPointerType = 'mouse'; - // TODO@Alex: replace the usage of MSGesture here with something that works across all browsers - 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('pointerdown', (e: MSPointerEvent) => { - const pointerType = e.pointerType; - if (pointerType === 'mouse') { - this._lastPointerType = 'mouse'; - return; - } else if (pointerType === '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)); + this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'pointerdown', (e: any) => { + const pointerType = e.pointerType; + if (pointerType === 'mouse') { + this._lastPointerType = 'mouse'; + return; + } else if (pointerType === 'touch') { + this._lastPointerType = 'touch'; + } else { + this._lastPointerType = 'pen'; } - }, 100); - this._lastPointerType = 'mouse'; - } + })); - public _onMouseDown(e: EditorMouseEvent): void { - if (this._lastPointerType === 'mouse') { - super._onMouseDown(e); - } + // PonterEvents + const pointerEvents = new EditorPointerEventFactory(this.viewHelper.viewDomNode); + + this._register(pointerEvents.onPointerMoveThrottled(this.viewHelper.viewDomNode, + (e) => this._onMouseMove(e), + createMouseMoveEventMerger(this.mouseTargetFactory), MouseHandler.MOUSE_MOVE_MINIMUM_TIME)); + this._register(pointerEvents.onPointerUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e))); + this._register(pointerEvents.onPointerLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e))); + this._register(pointerEvents.onPointerDown(this.viewHelper.viewDomNode, (e) => this._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); + private onTap(event: GestureEvent): void { + if (!event.initialTarget || !this.viewHelper.linesContentDomNode.contains(event.initialTarget)) { + return; } - // 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(); + + event.preventDefault(); + this.viewHelper.focusTextArea(); + const target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false); + + if (target.position) { + // this.viewController.moveTo(target.position); + this.viewController.dispatchMouse({ + position: target.position, + mouseColumn: target.position.column, + startedOnLineNumbers: false, + mouseDownCount: event.tapCount, + inSelectionMode: false, + altKey: false, + ctrlKey: false, + metaKey: false, + shiftKey: false, + + leftButton: false, + middleButton: false, }); } } - private _onGestureChange(e: IThrottledGestureEvent): void { - this._context.viewLayout.deltaScrollNow(-e.translationX, -e.translationY); + private onChange(e: GestureEvent): void { + if (this._lastPointerType === 'touch') { + this._context.model.deltaScrollNow(-e.translationX, -e.translationY); + } } - public dispose(): void { - window.clearTimeout(this._installGestureHandlerTimeout); - super.dispose(); + public _onMouseDown(e: EditorMouseEvent): void { + if (e.target && this.viewHelper.linesContentDomNode.contains(e.target) && this._lastPointerType === 'touch') { + return; + } + + super._onMouseDown(e); } } @@ -210,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); } } @@ -219,11 +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 ((window).TouchEvent) { + if ((platform.isIOS && BrowserFeatures.pointerEvents)) { + this.handler = this._register(new PointerEventHandler(context, viewController, viewHelper)); + } 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 ec222be435488..220c6c440ea8d 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -17,7 +17,7 @@ import { ViewController } from 'vs/editor/browser/view/viewController'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; -import { RenderLineNumbersType, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { RenderLineNumbersType, EditorOption, IComputedEditorOptions, EditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; import { Position } from 'vs/editor/common/core/position'; @@ -29,6 +29,8 @@ import { RenderingContext, RestrictedRenderingContext, HorizontalPosition } from 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; @@ -52,7 +54,7 @@ class VisibleTextAreaData { } } -const canUseZeroSizeTextarea = (browser.isEdgeOrIE || browser.isFirefox); +const canUseZeroSizeTextarea = (browser.isEdge || browser.isFirefox); export class TextAreaHandler extends ViewPart { @@ -61,7 +63,8 @@ export class TextAreaHandler extends ViewPart { private _scrollLeft: number; private _scrollTop: number; - private _accessibilitySupport: AccessibilitySupport; + private _accessibilitySupport!: AccessibilitySupport; + private _accessibilityPageSize!: number; private _contentLeft: number; private _contentWidth: number; private _contentHeight: number; @@ -75,6 +78,13 @@ export class TextAreaHandler extends ViewPart { */ private _visibleTextArea: VisibleTextAreaData | null; private _selections: Selection[]; + private _modelSelections: Selection[]; + + /** + * The position at which the textarea was rendered. + * This is useful for hit-testing and determining the mouse position. + */ + private _lastRenderPosition: Position | null; public readonly textArea: FastDomNode; public readonly textAreaCover: FastDomNode; @@ -91,10 +101,10 @@ export class TextAreaHandler extends ViewPart { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); - this._accessibilitySupport = options.get(EditorOption.accessibilitySupport); + this._setAccessibilityOptions(options); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; - this._contentHeight = layoutInfo.contentHeight; + this._contentHeight = layoutInfo.height; this._fontInfo = options.get(EditorOption.fontInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard); @@ -102,11 +112,13 @@ export class TextAreaHandler extends ViewPart { this._visibleTextArea = null; this._selections = [new Selection(1, 1, 1, 1)]; + this._modelSelections = [new Selection(1, 1, 1, 1)]; + this._lastRenderPosition = null; // 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'); @@ -114,6 +126,7 @@ export class TextAreaHandler extends ViewPart { this.textArea.setAttribute('spellcheck', 'false'); this.textArea.setAttribute('aria-label', this._getAriaLabel(options)); 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'); @@ -139,34 +152,33 @@ export class TextAreaHandler extends ViewPart { const textAreaInputHost: ITextAreaInputHost = { getDataToCopy: (generateHTML: boolean): ClipboardDataToCopy => { - const rawTextToCopy = this._context.model.getPlainTextToCopy(this._selections, this._emptySelectionClipboard, platform.isWindows); + const rawTextToCopy = this._context.model.getPlainTextToCopy(this._modelSelections, this._emptySelectionClipboard, platform.isWindows); const newLineCharacter = this._context.model.getEOL(); - const isFromEmptySelection = (this._emptySelectionClipboard && this._selections.length === 1 && this._selections[0].isEmpty()); + const isFromEmptySelection = (this._emptySelectionClipboard && this._modelSelections.length === 1 && this._modelSelections[0].isEmpty()); const multicursorText = (Array.isArray(rawTextToCopy) ? rawTextToCopy : null); const text = (Array.isArray(rawTextToCopy) ? rawTextToCopy.join(newLineCharacter) : rawTextToCopy); let html: string | null | undefined = undefined; + let mode: string | null = null; if (generateHTML) { if (CopyOptions.forceCopyWithSyntaxHighlighting || (this._copyWithSyntaxHighlighting && text.length < 65536)) { - html = this._context.model.getHTMLToCopy(this._selections, this._emptySelectionClipboard); + const richText = this._context.model.getRichTextToCopy(this._modelSelections, this._emptySelectionClipboard); + if (richText) { + html = richText.html; + mode = richText.mode; + } } } return { isFromEmptySelection, multicursorText, text, - html + html, + 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 @@ -189,7 +201,7 @@ export class TextAreaHandler extends ViewPart { return TextAreaState.EMPTY; } - return PagedScreenReaderStrategy.fromEditorSelection(currentState, simpleModel, this._selections[0], this._accessibilitySupport === AccessibilitySupport.Unknown); + return PagedScreenReaderStrategy.fromEditorSelection(currentState, simpleModel, this._selections[0], this._accessibilityPageSize, this._accessibilitySupport === AccessibilitySupport.Unknown); }, deduceModelPosition: (viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position => { @@ -210,40 +222,42 @@ export class TextAreaHandler extends ViewPart { this._register(this._textAreaInput.onPaste((e: IPasteData) => { let pasteOnNewLine = false; let multicursorText: string[] | null = null; + let mode: string | null = null; if (e.metadata) { pasteOnNewLine = (this._emptySelectionClipboard && !!e.metadata.isFromEmptySelection); multicursorText = (typeof e.metadata.multicursorText !== 'undefined' ? e.metadata.multicursorText : null); + mode = e.metadata.mode; } - this._viewController.paste('keyboard', e.text, pasteOnNewLine, multicursorText); + 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); @@ -258,13 +272,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); @@ -280,16 +294,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); })); } @@ -329,21 +343,33 @@ 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); } + private _setAccessibilityOptions(options: IComputedEditorOptions): void { + 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 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; + } + } + // --- begin event handlers public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); - this._accessibilitySupport = options.get(EditorOption.accessibilitySupport); + this._setAccessibilityOptions(options); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; - this._contentHeight = layoutInfo.contentHeight; + this._contentHeight = layoutInfo.height; this._fontInfo = options.get(EditorOption.fontInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard); @@ -366,6 +392,7 @@ export class TextAreaHandler extends ViewPart { } public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { this._selections = e.selections.slice(0); + this._modelSelections = e.modelSelections.slice(0); this._textAreaInput.writeScreenReaderContent('selection changed'); return true; } @@ -406,13 +433,37 @@ export class TextAreaHandler extends ViewPart { this._textAreaInput.focusTextArea(); } + public refreshFocusState() { + this._textAreaInput.refreshFocusState(); + } + + public getLastRenderData(): Position | null { + return this._lastRenderPosition; + } + + public setAriaOptions(options: IEditorAriaOptions): void { + if (options.activeDescendant) { + this.textArea.setAttribute('aria-haspopup', 'true'); + this.textArea.setAttribute('aria-autocomplete', 'list'); + this.textArea.setAttribute('aria-activedescendant', options.activeDescendant); + } else { + this.textArea.setAttribute('aria-haspopup', 'false'); + this.textArea.setAttribute('aria-autocomplete', 'both'); + this.textArea.removeAttribute('aria-activedescendant'); + } + if (options.role) { + this.textArea.setAttribute('role', options.role); + } + } + // --- end view API + private _primaryCursorPosition: Position = new Position(1, 1); private _primaryCursorVisibleRange: HorizontalPosition | null = null; public prepareRender(ctx: RenderingContext): void { - const primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn); - this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(primaryCursorPosition); + this._primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn); + this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(this._primaryCursorPosition); } public render(ctx: RestrictedRenderingContext): void { @@ -424,6 +475,7 @@ export class TextAreaHandler extends ViewPart { if (this._visibleTextArea) { // The text area is visible for composition reasons this._renderInsideEditor( + null, this._visibleTextArea.top - this._scrollTop, this._contentLeft + this._visibleTextArea.left - this._scrollLeft, this._visibleTextArea.width, @@ -458,6 +510,7 @@ export class TextAreaHandler extends ViewPart { // For the popup emoji input, we will make the text area as high as the line height // We will also make the fontSize and lineHeight the correct dimensions to help with the placement of these pickers this._renderInsideEditor( + this._primaryCursorPosition, top, left, canUseZeroSizeTextarea ? 0 : 1, this._lineHeight ); @@ -465,12 +518,14 @@ export class TextAreaHandler extends ViewPart { } this._renderInsideEditor( + this._primaryCursorPosition, top, left, canUseZeroSizeTextarea ? 0 : 1, canUseZeroSizeTextarea ? 0 : 1 ); } - private _renderInsideEditor(top: number, left: number, width: number, height: number): void { + private _renderInsideEditor(renderedPosition: Position | null, top: number, left: number, width: number, height: number): void { + this._lastRenderPosition = renderedPosition; const ta = this.textArea; const tac = this.textAreaCover; @@ -488,6 +543,7 @@ export class TextAreaHandler extends ViewPart { } private _renderAtTopLeft(): void { + this._lastRenderPosition = null; const ta = this.textArea; const tac = this.textAreaCover; diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 23f3d0cc2f71a..7b2868aa70105 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -16,6 +16,7 @@ import * as strings from 'vs/base/common/strings'; import { ITextAreaWrapper, ITypeData, TextAreaState } from 'vs/editor/browser/controller/textAreaState'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; export interface ICompositionData { data: string; @@ -40,12 +41,14 @@ export interface ClipboardDataToCopy { multicursorText: string[] | null | undefined; text: string; html: string | null | undefined; + mode: string | null; } export interface ClipboardStoredMetadata { version: 1; isFromEmptySelection: boolean | undefined; multicursorText: string[] | null | undefined; + mode: string | null; } export interface ITextAreaInputHost { @@ -54,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; @@ -82,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; @@ -105,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 @@ -136,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; @@ -152,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; @@ -162,11 +155,10 @@ export class TextAreaInput extends Disposable { private _isDoingComposition: boolean; private _nextCommand: ReadFromTextArea; - constructor(host: ITextAreaInputHost, textArea: FastDomNode) { + constructor(host: ITextAreaInputHost, private textArea: FastDomNode) { 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; @@ -177,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(); } @@ -189,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); })); @@ -197,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)]; }; /** @@ -239,15 +256,7 @@ export class TextAreaInput extends Disposable { // 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; } @@ -255,10 +264,8 @@ 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); @@ -272,11 +279,14 @@ export class TextAreaInput extends Disposable { })); 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); + const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/false); this._textAreaState = newState; this._onType.fire(typeInput); } else { @@ -287,7 +297,7 @@ export class TextAreaInput extends Disposable { // 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); } @@ -300,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'); @@ -312,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; @@ -324,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; @@ -334,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'); @@ -345,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'); @@ -372,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. // @@ -408,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; } @@ -476,12 +474,26 @@ 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 { return this._hasFocus; } + public refreshFocusState(): void { + 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); + } + } + private _setHasFocus(newHasFocus: boolean): void { if (this._hasFocus === newHasFocus) { // no change @@ -533,11 +545,12 @@ export class TextAreaInput extends Disposable { } private _ensureClipboardGetsEditorSelection(e: ClipboardEvent): void { - const dataToCopy = this._host.getDataToCopy(ClipboardEventUtils.canUseTextData(e) && browser.hasClipboardSupport()); + const dataToCopy = this._host.getDataToCopy(ClipboardEventUtils.canUseTextData(e) && BrowserFeatures.clipboard.richText); const storedMetadata: ClipboardStoredMetadata = { version: 1, isFromEmptySelection: dataToCopy.isFromEmptySelection, - multicursorText: dataToCopy.multicursorText + multicursorText: dataToCopy.multicursorText, + mode: dataToCopy.mode }; InMemoryClipboardMetadataManager.INSTANCE.set( // When writing "LINE\r\n" to the clipboard and then pasting, @@ -603,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!'); @@ -680,7 +694,15 @@ 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; diff --git a/src/vs/editor/browser/controller/textAreaState.ts b/src/vs/editor/browser/controller/textAreaState.ts index b907a3bfa6a69..f46e77022357c 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); @@ -226,26 +214,24 @@ export class TextAreaState { } export class PagedScreenReaderStrategy { - private static readonly _LINES_PER_PAGE = 10; - - private static _getPageOfLine(lineNumber: number): number { - return Math.floor((lineNumber - 1) / PagedScreenReaderStrategy._LINES_PER_PAGE); + private static _getPageOfLine(lineNumber: number, linesPerPage: number): number { + return Math.floor((lineNumber - 1) / linesPerPage); } - private static _getRangeForPage(page: number): Range { - const offset = page * PagedScreenReaderStrategy._LINES_PER_PAGE; + private static _getRangeForPage(page: number, linesPerPage: number): Range { + const offset = page * linesPerPage; const startLineNumber = offset + 1; - const endLineNumber = offset + PagedScreenReaderStrategy._LINES_PER_PAGE; + const endLineNumber = offset + linesPerPage; return new Range(startLineNumber, 1, endLineNumber + 1, 1); } - public static fromEditorSelection(previousState: TextAreaState, model: ISimpleModel, selection: Range, trimLongText: boolean): TextAreaState { + public static fromEditorSelection(previousState: TextAreaState, model: ISimpleModel, selection: Range, linesPerPage: number, trimLongText: boolean): TextAreaState { - const selectionStartPage = PagedScreenReaderStrategy._getPageOfLine(selection.startLineNumber); - const selectionStartPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionStartPage); + const selectionStartPage = PagedScreenReaderStrategy._getPageOfLine(selection.startLineNumber, linesPerPage); + const selectionStartPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionStartPage, linesPerPage); - const selectionEndPage = PagedScreenReaderStrategy._getPageOfLine(selection.endLineNumber); - const selectionEndPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionEndPage); + const selectionEndPage = PagedScreenReaderStrategy._getPageOfLine(selection.endLineNumber, linesPerPage); + const selectionEndPageRange = PagedScreenReaderStrategy._getRangeForPage(selectionEndPage, linesPerPage); const pretextRange = selectionStartPageRange.intersectRanges(new Range(1, 1, selection.startLineNumber, selection.startColumn))!; let pretext = model.getValueInRange(pretextRange, EndOfLinePreference.LF); diff --git a/src/vs/editor/browser/core/editorState.ts b/src/vs/editor/browser/core/editorState.ts index 9d469299bbbfe..5d4c1ffb0e513 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 df5795ffe28f8..1f260f9b6cd93 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/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 50eed2efb08e5..aa1ea51f92735 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -6,19 +6,19 @@ 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 } from 'vs/editor/common/config/editorOptions'; -import { ICursors } from 'vs/editor/common/controller/cursorCommon'; +import { OverviewRulerPosition, ConfigurationChangedEvent, EditorLayoutInfo, IComputedEditorOptions, EditorOption, FindComputedEditorOptionValueById, IEditorOptions, IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; 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/whitespaceComputer'; +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. @@ -308,6 +308,14 @@ export interface IPartialEditorMouseEvent { readonly target: IMouseTarget | null; } +/** + * A paste event originating from the editor. + */ +export interface IPasteEvent { + readonly range: Range; + readonly mode: string | null; +} + /** * An overview ruler * @internal @@ -319,6 +327,35 @@ export interface IOverviewRuler { setLayout(position: OverviewRulerPosition): void; } +/** + * Editor aria options. + * @internal + */ +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; +} + /** * A rich code editor. */ @@ -408,11 +445,11 @@ export interface ICodeEditor extends editorCommon.IEditor { /** * An event emitted after composition has started. */ - onCompositionStart(listener: () => void): IDisposable; + onDidCompositionStart(listener: () => void): IDisposable; /** * An event emitted after composition has ended. */ - onCompositionEnd(listener: () => void): IDisposable; + onDidCompositionEnd(listener: () => void): IDisposable; /** * An event emitted when editing failed because the editor is read-only. * @event @@ -422,9 +459,8 @@ export interface ICodeEditor extends editorCommon.IEditor { /** * An event emitted when users paste text in the editor. * @event - * @internal */ - onDidPaste(listener: (range: Range) => void): IDisposable; + onDidPaste(listener: (e: IPasteEvent) => void): IDisposable; /** * An event emitted on a "mouseup". * @event @@ -483,6 +519,11 @@ export interface ICodeEditor extends editorCommon.IEditor { * @event */ onDidLayoutChange(listener: (e: EditorLayoutInfo) => void): IDisposable; + /** + * An event emitted when the content width or content height in the editor has changed. + * @event + */ + onDidContentSizeChange(listener: (e: editorCommon.IContentSizeChangedEvent) => void): IDisposable; /** * An event emitted when the scroll in the editor has changed. * @event @@ -533,12 +574,12 @@ export interface ICodeEditor extends editorCommon.IEditor { setModel(model: ITextModel | null): void; /** - * @internal + * Gets all the editor computed options. */ getOptions(): IComputedEditorOptions; /** - * @internal + * Gets a specific editor option. */ getOption(id: T): FindComputedEditorOptionValueById; @@ -547,6 +588,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` @@ -559,6 +610,11 @@ export interface ICodeEditor extends editorCommon.IEditor { */ setValue(newValue: string): void; + /** + * Get the width of the editor's content. + * This is information that is "erased" when computing `scrollWidth = Math.max(contentWidth, width)` + */ + getContentWidth(): number; /** * Get the scrollWidth of the editor's viewport. */ @@ -568,6 +624,11 @@ export interface ICodeEditor extends editorCommon.IEditor { */ getScrollLeft(): number; + /** + * Get the height of the editor's content. + * This is information that is "erased" when computing `scrollHeight = Math.max(contentHeight, height)` + */ + getContentHeight(): number; /** * Get the scrollHeight of the editor's viewport. */ @@ -580,15 +641,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. @@ -603,7 +664,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. @@ -617,19 +678,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). @@ -668,6 +729,11 @@ export interface ICodeEditor extends editorCommon.IEditor { */ getVisibleRanges(): Range[]; + /** + * @internal + */ + getVisibleRangesPlusViewportAboveBelow(): Range[]; + /** * Get the view zones. * @internal @@ -690,11 +756,22 @@ export interface ICodeEditor extends editorCommon.IEditor { */ setHiddenAreas(ranges: IRange[]): void; + /** + * Sets the editor aria options, primarily the active descendent. + * @internal + */ + setAriaOptions(options: IEditorAriaOptions): void; + /** * @internal */ getTelemetryData(): { [key: string]: any } | undefined; + /** + * Returns the editor's container dom node + */ + getContainerDomNode(): HTMLElement; + /** * Returns the editor's dom node */ @@ -806,7 +883,7 @@ export interface IActiveCodeEditor extends ICodeEditor { /** * @internal */ - _getCursors(): ICursors; + _getViewModel(): IViewModel; /** * Get all the decorations on a line (filtering out decorations from other editors). @@ -938,6 +1015,11 @@ export interface IDiffEditor extends editorCommon.IEditor { * If the diff computation is not finished or the model is missing, will return null. */ getDiffLineInformationForModified(lineNumber: number): IDiffLineInformation | null; + + /** + * Update the editor's options after the editor has been created. + */ + updateOptions(newOptions: IDiffEditorOptions): void; } /** @@ -962,6 +1044,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 */ @@ -976,3 +1068,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/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 92357091f0861..4042ae8262c26 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -84,7 +84,7 @@ export class EditorMouseEvent extends StandardMouseEvent { } export interface EditorMouseEventMerger { - (lastEvent: EditorMouseEvent, currentEvent: EditorMouseEvent): EditorMouseEvent; + (lastEvent: EditorMouseEvent | null, currentEvent: EditorMouseEvent): EditorMouseEvent; } export class EditorMouseEventFactory { @@ -124,13 +124,51 @@ export class EditorMouseEventFactory { } public onMouseMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, this._create(currentEvent)); }; return dom.addDisposableThrottledListener(target, 'mousemove', callback, myMerger, minimumTimeMs); } } +export class EditorPointerEventFactory { + + private readonly _editorViewDomNode: HTMLElement; + + constructor(editorViewDomNode: HTMLElement) { + this._editorViewDomNode = editorViewDomNode; + } + + private _create(e: MouseEvent): EditorMouseEvent { + return new EditorMouseEvent(e, this._editorViewDomNode); + } + + public onPointerUp(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'pointerup', (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onPointerDown(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableListener(target, 'pointerdown', (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onPointerLeave(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { + return dom.addDisposableNonBubblingPointerOutListener(target, (e: MouseEvent) => { + callback(this._create(e)); + }); + } + + public onPointerMoveThrottled(target: HTMLElement, callback: (e: EditorMouseEvent) => void, merger: EditorMouseEventMerger, minimumTimeMs: number): IDisposable { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { + return merger(lastEvent, this._create(currentEvent)); + }; + return dom.addDisposableThrottledListener(target, 'pointermove', callback, myMerger, minimumTimeMs); + } +} + export class GlobalEditorMouseMoveMonitor extends Disposable { private readonly _editorViewDomNode: HTMLElement; @@ -144,7 +182,13 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { this._keydownListener = null; } - public startMonitoring(merger: EditorMouseEventMerger, mouseMoveCallback: (e: EditorMouseEvent) => void, onStopCallback: () => void): void { + public startMonitoring( + initialElement: HTMLElement, + initialButtons: number, + merger: EditorMouseEventMerger, + mouseMoveCallback: (e: EditorMouseEvent) => void, + onStopCallback: () => void + ): void { // Add a <> keydown event listener that will cancel the monitoring // if something other than a modifier key is pressed @@ -157,11 +201,11 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { this._globalMouseMoveMonitor.stopMonitoring(true); }, true); - const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent, currentEvent: MouseEvent): EditorMouseEvent => { + const myMerger: dom.IEventMerger = (lastEvent: EditorMouseEvent | null, currentEvent: MouseEvent): EditorMouseEvent => { return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); }; - this._globalMouseMoveMonitor.startMonitoring(myMerger, mouseMoveCallback, () => { + this._globalMouseMoveMonitor.startMonitoring(initialElement, initialButtons, myMerger, mouseMoveCallback, () => { this._keydownListener!.dispose(); onStopCallback(); }); diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 8f2d7c90482f3..e4e081bd30091 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 { IConstructorSignature1, ServicesAccessor as InstantiationServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +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, 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 } from 'vs/base/common/types'; +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,51 +44,49 @@ 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 ICommandMenubarOptions { +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; - menubarOpts?: ICommandMenubarOptions; + 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 _menubarOpts: ICommandMenubarOptions | undefined; + private readonly _menuOpts: ICommandMenuOptions | ICommandMenuOptions[] | undefined; private readonly _description: ICommandHandlerDescription | undefined; constructor(opts: ICommandOptions) { this.id = opts.id; this.precondition = opts.precondition; this._kbOpts = opts.kbOpts; - this._menubarOpts = opts.menubarOpts; + this._menuOpts = opts.menuOpts; this._description = opts.description; } public register(): void { - if (this._menubarOpts) { - MenuRegistry.appendMenuItem(this._menubarOpts.menuId, { - group: this._menubarOpts.group, - command: { - id: this.id, - title: this._menubarOpts.title, - // precondition: this.precondition - }, - when: this._menubarOpts.when, - order: this._menubarOpts.order - }); + if (Array.isArray(this._menuOpts)) { + this._menuOpts.forEach(this._registerMenuItem, this); + } else if (this._menuOpts) { + this._registerMenuItem(this._menuOpts); } if (this._kbOpts) { @@ -100,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, @@ -119,11 +123,85 @@ export abstract class Command { } } + private _registerMenuItem(item: ICommandMenuOptions): void { + MenuRegistry.appendMenuItem(item.menuId, { + group: item.group, + command: { + id: this.id, + title: item.title, + icon: item.icon + // precondition: this.precondition + }, + when: item.when, + order: item.order + }); + } + public abstract runCommand(accessor: ServicesAccessor, args: any): void | Promise; } //#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; + +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) { + if (impl[1](accessor, args)) { + return; + } + } + } +} + +//#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 { @@ -184,44 +262,59 @@ export abstract class EditorCommand extends Command { //#region EditorAction -export interface IEditorCommandMenuOptions { +export interface IEditorActionContextMenuOptions { group: string; order: number; - when?: ContextKeyExpr; + when?: ContextKeyExpression; + menuId?: MenuId; } export interface IActionOptions extends ICommandOptions { label: string; alias: string; - menuOpts?: IEditorCommandMenuOptions; + contextMenuOpts?: IEditorActionContextMenuOptions | IEditorActionContextMenuOptions[]; } + export abstract class EditorAction extends EditorCommand { + private static convertOptions(opts: IActionOptions): ICommandOptions { + + let menuOpts: ICommandMenuOptions[]; + if (Array.isArray(opts.menuOpts)) { + menuOpts = opts.menuOpts; + } else if (opts.menuOpts) { + menuOpts = [opts.menuOpts]; + } else { + menuOpts = []; + } + + function withDefaults(item: Partial): ICommandMenuOptions { + if (!item.menuId) { + item.menuId = MenuId.EditorContext; + } + if (!item.title) { + item.title = opts.label; + } + item.when = ContextKeyExpr.and(opts.precondition, item.when); + return item; + } + + if (Array.isArray(opts.contextMenuOpts)) { + menuOpts.push(...opts.contextMenuOpts.map(withDefaults)); + } else if (opts.contextMenuOpts) { + menuOpts.push(withDefaults(opts.contextMenuOpts)); + } + + opts.menuOpts = menuOpts; + return opts; + } + public readonly label: string; public readonly alias: string; - private readonly menuOpts: IEditorCommandMenuOptions | undefined; constructor(opts: IActionOptions) { - super(opts); + super(EditorAction.convertOptions(opts)); this.label = opts.label; this.alias = opts.alias; - this.menuOpts = opts.menuOpts; - } - - public register(): void { - - if (this.menuOpts) { - MenuRegistry.appendMenuItem(MenuId.EditorContext, { - command: { - id: this.id, - title: this.label - }, - when: ContextKeyExpr.and(this.precondition, this.menuOpts.when), - group: this.menuOpts.group, - order: this.menuOpts.order - }); - } - - super.register(); } public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise { @@ -244,8 +337,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) { @@ -290,24 +447,85 @@ export function registerDefaultLanguageCommand(id: string, handler: (model: ITex }); } +export function registerModelAndPositionCommand(id: string, handler: (model: ITextModel, position: Position, ...args: any[]) => any) { + CommandsRegistry.registerCommand(id, function (accessor, ...args) { + + const [resource, position] = args; + assertType(URI.isUri(resource)); + assertType(Position.isIPosition(position)); + + const model = accessor.get(IModelService).getModel(resource); + if (model) { + const editorPosition = Position.lift(position); + return handler(model, editorPosition, ...args.slice(2)); + } + + return accessor.get(ITextModelService).createModelReference(resource).then(reference => { + return new Promise((resolve, reject) => { + try { + const result = handler(reference.object.textEditorModel, Position.lift(position), args.slice(2)); + resolve(result); + } catch (err) { + reject(err); + } + }).finally(() => { + reference.dispose(); + }); + }); + }); +} + +export function registerModelCommand(id: string, handler: (model: ITextModel, ...args: any[]) => any) { + CommandsRegistry.registerCommand(id, function (accessor, ...args) { + + const [resource] = args; + assertType(URI.isUri(resource)); + + const model = accessor.get(IModelService).getModel(resource); + if (model) { + return handler(model, ...args.slice(1)); + } + + return accessor.get(ITextModelService).createModelReference(resource).then(reference => { + return new Promise((resolve, reject) => { + try { + const result = handler(reference.object.textEditorModel, args.slice(1)); + resolve(result); + } catch (err) { + reject(err); + } + }).finally(() => { + reference.dispose(); + }); + }); + }); +} + export function registerEditorCommand(editorCommand: T): T { EditorContributionRegistry.INSTANCE.registerEditorCommand(editorCommand); 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 { EditorContributionRegistry.INSTANCE.registerEditorAction(editorAction); } -export function registerEditorContribution(id: string, ctor: IEditorContributionCtor): void { +export function registerEditorContribution(id: string, ctor: { new(editor: ICodeEditor, ...services: Services): IEditorContribution }): void { EditorContributionRegistry.INSTANCE.registerEditorContribution(id, ctor); } -export function registerDiffEditorContribution(id: string, ctor: IDiffEditorContributionCtor): void { +export function registerDiffEditorContribution(id: string, ctor: { new(editor: IDiffEditor, ...services: Services): IEditorContribution }): void { EditorContributionRegistry.INSTANCE.registerDiffEditorContribution(id, ctor); } @@ -355,16 +573,16 @@ class EditorContributionRegistry { this.editorCommands = Object.create(null); } - public registerEditorContribution(id: string, ctor: IEditorContributionCtor): void { - this.editorContributions.push({ id, ctor }); + public registerEditorContribution(id: string, ctor: { new(editor: ICodeEditor, ...services: Services): IEditorContribution }): void { + this.editorContributions.push({ id, ctor: ctor as IEditorContributionCtor }); } public getEditorContributions(): IEditorContributionDescription[] { return this.editorContributions.slice(0); } - public registerDiffEditorContribution(id: string, ctor: IDiffEditorContributionCtor): void { - this.diffEditorContributions.push({ id, ctor }); + public registerDiffEditorContribution(id: string, ctor: { new(editor: IDiffEditor, ...services: Services): IEditorContribution }): void { + this.diffEditorContributions.push({ id, ctor: ctor as IDiffEditorContributionCtor }); } public getDiffEditorContributions(): IDiffEditorContributionDescription[] { @@ -391,3 +609,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 53c5bac6f2817..f48a9cff839a0 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; @@ -89,11 +90,34 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC return editorWithWidgetFocus; } - abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void; + abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; abstract removeDecorationType(key: string): void; 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(); @@ -120,12 +144,22 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC return this._transientWatchers[uri].get(key); } + public getTransientModelProperties(model: ITextModel): [string, any][] | undefined { + const uri = model.uri.toString(); + + if (!this._transientWatchers.hasOwnProperty(uri)) { + return undefined; + } + + return this._transientWatchers[uri].keys().map(key => [key, this._transientWatchers[uri].get(key)]); + } + _removeWatcher(w: ModelTransientSettingWatcher): void { delete this._transientWatchers[w.uri]; } 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 { @@ -145,4 +179,8 @@ export class ModelTransientSettingWatcher { public get(key: string): any { return this._values[key]; } + + public keys(): string[] { + return Object.keys(this._values); + } } diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index ada4ed7b23d2b..7a1f3346e46f9 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -4,25 +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 = (edits: ResourceEdit[], options?: IBulkEditOptions) => Promise; + export interface IBulkEditService { - _serviceBrand: undefined; + readonly _serviceBrand: undefined; - apply(edit: WorkspaceEdit, options?: IBulkEditOptions): Promise; -} + hasPreviewHandler(): boolean; + setPreviewHandler(handler: IBulkEditPreviewHandler): IDisposable; + + 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 0cfba7fd7fbb0..0174886d201bb 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; @@ -37,13 +38,17 @@ export interface ICodeEditorService { */ getFocusedCodeEditor(): ICodeEditor | null; - registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void; + registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; 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 c7cf1da3f4535..464c1a33d410b 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -11,34 +11,122 @@ 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'; -export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { +export class RefCountedStyleSheet { + + private readonly _parent: CodeEditorServiceImpl; + private readonly _editorId: string; + private readonly _styleSheet: HTMLStyleElement; + private _refCount: number; + + constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) { + this._parent = parent; + this._editorId = editorId; + this._styleSheet = styleSheet; + this._refCount = 0; + } + + public ref(): void { + this._refCount++; + } + + public unref(): void { + this._refCount--; + if (this._refCount === 0) { + 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); + } +} + +export class GlobalStyleSheet { private readonly _styleSheet: HTMLStyleElement; + + constructor(styleSheet: HTMLStyleElement) { + this._styleSheet = styleSheet; + } + + public ref(): void { + } + + 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 { + + private _globalStyleSheet: GlobalStyleSheet | null; private readonly _decorationOptionProviders = new Map(); + private readonly _editorStyleSheets = new Map(); private readonly _themeService: IThemeService; - constructor(@IThemeService themeService: IThemeService, styleSheet = dom.createStyleSheet()) { + constructor(@IThemeService themeService: IThemeService, styleSheet: GlobalStyleSheet | null = null) { super(); - this._styleSheet = styleSheet; + this._globalStyleSheet = styleSheet ? styleSheet : null; this._themeService = themeService; } - public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { + private _getOrCreateGlobalStyleSheet(): GlobalStyleSheet { + if (!this._globalStyleSheet) { + this._globalStyleSheet = new GlobalStyleSheet(dom.createStyleSheet()); + } + return this._globalStyleSheet; + } + + private _getOrCreateStyleSheet(editor: ICodeEditor | undefined): GlobalStyleSheet | RefCountedStyleSheet { + if (!editor) { + return this._getOrCreateGlobalStyleSheet(); + } + const domNode = editor.getContainerDomNode(); + if (!dom.isInShadowDOM(domNode)) { + return this._getOrCreateGlobalStyleSheet(); + } + const editorId = editor.getId(); + if (!this._editorStyleSheets.has(editorId)) { + const refCountedStyleSheet = new RefCountedStyleSheet(this, editorId, dom.createStyleSheet(domNode)); + this._editorStyleSheets.set(editorId, refCountedStyleSheet); + } + return this._editorStyleSheets.get(editorId)!; + } + + _removeEditorStyleSheets(editorId: string): void { + this._editorStyleSheets.delete(editorId); + } + + public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void { let provider = this._decorationOptionProviders.get(key); if (!provider) { + const styleSheet = this._getOrCreateStyleSheet(editor); const providerArgs: ProviderArguments = { - styleSheet: this._styleSheet, + styleSheet: styleSheet, key: key, parentTypeKey: parentTypeKey, options: options || Object.create(null) }; if (!parentTypeKey) { - provider = new DecorationTypeOptionsProvider(this._themeService, providerArgs); + provider = new DecorationTypeOptionsProvider(this._themeService, styleSheet, providerArgs); } else { - provider = new DecorationSubTypeOptionsProvider(this._themeService, providerArgs); + provider = new DecorationSubTypeOptionsProvider(this._themeService, styleSheet, providerArgs); } this._decorationOptionProviders.set(key, provider); } @@ -66,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 { @@ -76,13 +164,16 @@ interface IModelDecorationOptionsProvider extends IDisposable { class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider { + private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet; public refCount: number; private readonly _parentTypeKey: string | undefined; private _beforeContentRules: DecorationCSSRules | null; private _afterContentRules: DecorationCSSRules | null; - constructor(themeService: IThemeService, providerArgs: ProviderArguments) { + constructor(themeService: IThemeService, styleSheet: GlobalStyleSheet | RefCountedStyleSheet, providerArgs: ProviderArguments) { + this._styleSheet = styleSheet; + this._styleSheet.ref(); this._parentTypeKey = providerArgs.parentTypeKey; this.refCount = 0; @@ -110,11 +201,12 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide this._afterContentRules.dispose(); this._afterContentRules = null; } + this._styleSheet.unref(); } } interface ProviderArguments { - styleSheet: HTMLStyleElement; + styleSheet: GlobalStyleSheet | RefCountedStyleSheet; key: string; parentTypeKey?: string; options: IDecorationRenderOptions; @@ -124,6 +216,7 @@ interface ProviderArguments { class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { private readonly _disposables = new DisposableStore(); + private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet; public refCount: number; public className: string | undefined; @@ -136,7 +229,9 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { public overviewRuler: IModelDecorationOverviewRulerOptions | undefined; public stickiness: TrackedRangeStickiness | undefined; - constructor(themeService: IThemeService, providerArgs: ProviderArguments) { + constructor(themeService: IThemeService, styleSheet: GlobalStyleSheet | RefCountedStyleSheet, providerArgs: ProviderArguments) { + this._styleSheet = styleSheet; + this._styleSheet.ref(); this.refCount = 0; const createCSSRules = (type: ModelDecorationCSSRuleType) => { @@ -202,6 +297,7 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { public dispose(): void { this._disposables.dispose(); + this._styleSheet.unref(); } } @@ -242,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; @@ -252,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; @@ -271,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(); }); @@ -336,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) { @@ -355,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 9902634b3e2f5..207e7a3bfa7a4 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -4,78 +4,170 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; import { parse } from 'vs/base/common/marshalling'; import { Schemas } from 'vs/base/common/network'; -import * as resources from 'vs/base/common/resources'; -import { equalsIgnoreCase } from 'vs/base/common/strings'; +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 { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions } from 'vs/platform/opener/common/opener'; +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'; -export class OpenerService extends Disposable implements IOpenerService { - _serviceBrand: undefined; +class CommandOpener implements IOpener { + + constructor(@ICommandService private readonly _commandService: ICommandService) { } + + async open(target: URI | string) { + if (!matchesScheme(target, Schemas.command)) { + return false; + } + // run command or bail out if command isn't known + if (typeof target === 'string') { + target = URI.parse(target); + } + // execute as command + let args: any = []; + try { + args = parse(decodeURIComponent(target.query)); + } catch { + // ignore and retry + try { + args = parse(target.query); + } catch { + // ignore error + } + } + if (!Array.isArray(args)) { + args = [args]; + } + await this._commandService.executeCommand(target.path, ...args); + return true; + } +} + +class EditorOpener implements IOpener { + + constructor(@ICodeEditorService private readonly _editorService: ICodeEditorService) { } + + async open(target: URI | string, options: OpenOptions) { + if (typeof target === 'string') { + target = URI.parse(target); + } + let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined; + const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment); + if (match) { + // support file:///some/file.js#73,84 + // support file:///some/file.js#L73 + selection = { + startLineNumber: parseInt(match[1]), + startColumn: match[2] ? parseInt(match[2]) : 1 + }; + // remove fragment + target = target.with({ fragment: '' }); + } + + if (target.scheme === Schemas.file) { + target = normalizePath(target); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) + } + + await this._editorService.openCodeEditor( + { resource: target, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, + this._editorService.getFocusedCodeEditor(), + options?.openToSide + ); + + return true; + } +} + +export class OpenerService implements IOpenerService { + + declare readonly _serviceBrand: undefined; private readonly _openers = new LinkedList(); private readonly _validators = new LinkedList(); private readonly _resolvers = new LinkedList(); + private _externalOpener: IExternalOpener; + constructor( - @ICodeEditorService private readonly _editorService: ICodeEditorService, - @ICommandService private readonly _commandService: ICommandService, + @ICodeEditorService editorService: ICodeEditorService, + @ICommandService commandService: ICommandService, ) { - super(); + // Default external opener is going through window.open() + this._externalOpener = { + openExternal: 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); + } + }; + + // Default opener: maito, http(s), command, and catch-all-editors + this._openers.push({ + open: async (target: URI | string, options?: OpenOptions) => { + if (options?.openExternal || matchesScheme(target, Schemas.mailto) || matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) { + // open externally + await this._doOpenExternal(target, options); + return true; + } + return false; + } + }); + this._openers.push(new CommandOpener(commandService)); + this._openers.push(new EditorOpener(editorService)); } registerOpener(opener: IOpener): IDisposable { - const remove = this._openers.push(opener); - + const remove = this._openers.unshift(opener); return { dispose: remove }; } registerValidator(validator: IValidator): IDisposable { const remove = this._validators.push(validator); - return { dispose: remove }; } registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable { const remove = this._resolvers.push(resolver); - return { dispose: remove }; } - async open(resource: URI, options?: OpenOptions): Promise { + setExternalOpener(externalOpener: IExternalOpener): void { + this._externalOpener = externalOpener; + } - // no scheme ?!? - if (!resource.scheme) { - return Promise.resolve(false); - } + async open(target: URI | string, options?: OpenOptions): Promise { // check with contributed validators for (const validator of this._validators.toArray()) { - if (!(await validator.shouldOpen(resource))) { + if (!(await validator.shouldOpen(target))) { return false; } } // check with contributed openers for (const opener of this._openers.toArray()) { - const handled = await opener.open(resource, options); + const handled = await opener.open(target, options); if (handled) { return true; } } - // use default openers - return this._doOpen(resource, options); + return false; } - async resolveExternalUri(resource: URI, options?: { readonly allowTunneling?: boolean }): Promise<{ resolved: URI, dispose(): void }> { + async resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise { for (const resolver of this._resolvers.toArray()) { const result = await resolver.resolveExternalUri(resource, options); if (result) { @@ -86,72 +178,19 @@ export class OpenerService extends Disposable implements IOpenerService { return { resolved: resource, dispose: () => { } }; } - private async _doOpen(resource: URI, options: OpenOptions | undefined): Promise { - const { scheme, path, query, fragment } = resource; - - if (equalsIgnoreCase(scheme, Schemas.mailto) || options?.openExternal) { - // open default mail application - return this._doOpenExternal(resource, options); - } - - if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) { - // open link in default browser - return this._doOpenExternal(resource, options); - } - - if (equalsIgnoreCase(scheme, Schemas.command)) { - // run command or bail out if command isn't known - if (!CommandsRegistry.getCommand(path)) { - throw new Error(`command '${path}' NOT known`); - } - // execute as command - let args: any = []; - try { - args = parse(query); - if (!Array.isArray(args)) { - args = [args]; - } - } catch (e) { - // ignore error - } - - await this._commandService.executeCommand(path, ...args); - - return true; - } + private async _doOpenExternal(resource: URI | string, options: OpenOptions | undefined): Promise { - // finally open in editor - let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined; - const match = /^L?(\d+)(?:,(\d+))?/.exec(fragment); - if (match) { - // support file:///some/file.js#73,84 - // support file:///some/file.js#L73 - selection = { - startLineNumber: parseInt(match[1]), - startColumn: match[2] ? parseInt(match[2]) : 1 - }; - // remove fragment - resource = resource.with({ fragment: '' }); - } + //todo@joh IExternalUriResolver should support `uri: URI | string` + const uri = typeof resource === 'string' ? URI.parse(resource) : resource; + const { resolved } = await this.resolveExternalUri(uri, options); - if (resource.scheme === Schemas.file) { - resource = resources.normalizePath(resource); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) + if (typeof resource === 'string' && uri.toString() === resolved.toString()) { + // open the url-string AS IS + return this._externalOpener.openExternal(resource); + } else { + // open URI using the toString(noEncode)+encodeURI-trick + return this._externalOpener.openExternal(encodeURI(resolved.toString(true))); } - - await this._editorService.openCodeEditor( - { resource, options: { selection, context: options?.fromUserGesture ? EditorOpenContext.USER : EditorOpenContext.API } }, - this._editorService.getFocusedCodeEditor(), - options?.openToSide - ); - - return true; - } - - private async _doOpenExternal(resource: URI, options: OpenOptions | undefined): Promise { - const { resolved } = await this.resolveExternalUri(resource, options); - dom.windowOpenNoOpener(encodeURI(resolved.toString(true))); - - return true; } dispose() { diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts new file mode 100644 index 0000000000000..45c568d0715f2 --- /dev/null +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -0,0 +1,304 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { createStringBuilder, IStringBuilder } from 'vs/editor/common/core/stringBuilder'; +import { CharCode } from 'vs/base/common/charCode'; +import * as strings from 'vs/base/common/strings'; +import { Configuration } from 'vs/editor/browser/config/configuration'; + +export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory { + + public static create(): DOMLineBreaksComputerFactory { + return new DOMLineBreaksComputerFactory(); + } + + constructor() { + } + + public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { + tabSize = tabSize | 0; //@perf + wrappingColumn = +wrappingColumn; //@perf + + let requests: string[] = []; + return { + addRequest: (lineText: string, previousLineBreakData: LineBreakData | null) => { + requests.push(lineText); + }, + finalize: () => { + return createLineBreaks(requests, fontInfo, tabSize, wrappingColumn, wrappingIndent); + } + }; + } +} + +function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: number, firstLineBreakColumn: number, wrappingIndent: WrappingIndent): (LineBreakData | null)[] { + if (firstLineBreakColumn === -1) { + const result: null[] = []; + for (let i = 0, len = requests.length; i < len; i++) { + result[i] = null; + } + return result; + } + + const overallWidth = Math.round(firstLineBreakColumn * fontInfo.typicalHalfwidthCharacterWidth); + + // Cannot respect WrappingIndent.Indent and WrappingIndent.DeepIndent because that would require + // two dom layouts, in order to first set the width of the first line, and then set the width of the wrapped lines + if (wrappingIndent === WrappingIndent.Indent || wrappingIndent === WrappingIndent.DeepIndent) { + wrappingIndent = WrappingIndent.Same; + } + + const containerDomNode = document.createElement('div'); + Configuration.applyFontInfoSlow(containerDomNode, fontInfo); + + const sb = createStringBuilder(10000); + const firstNonWhitespaceIndices: number[] = []; + const wrappedTextIndentLengths: number[] = []; + const renderLineContents: string[] = []; + const allCharOffsets: number[][] = []; + const allVisibleColumns: number[][] = []; + for (let i = 0; i < requests.length; i++) { + const lineContent = requests[i]; + + let firstNonWhitespaceIndex = 0; + let wrappedTextIndentLength = 0; + let width = overallWidth; + + if (wrappingIndent !== WrappingIndent.None) { + firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent); + if (firstNonWhitespaceIndex === -1) { + // all whitespace line + firstNonWhitespaceIndex = 0; + + } else { + // Track existing indent + + for (let i = 0; i < firstNonWhitespaceIndex; i++) { + const charWidth = ( + lineContent.charCodeAt(i) === CharCode.Tab + ? (tabSize - (wrappedTextIndentLength % tabSize)) + : 1 + ); + wrappedTextIndentLength += charWidth; + } + + const indentWidth = Math.ceil(fontInfo.spaceWidth * wrappedTextIndentLength); + + // Force sticking to beginning of line if no character would fit except for the indentation + if (indentWidth + fontInfo.typicalFullwidthCharacterWidth > overallWidth) { + firstNonWhitespaceIndex = 0; + wrappedTextIndentLength = 0; + } else { + width = overallWidth - indentWidth; + } + } + } + + const renderLineContent = lineContent.substr(firstNonWhitespaceIndex); + const tmp = renderLine(renderLineContent, wrappedTextIndentLength, tabSize, width, sb); + firstNonWhitespaceIndices[i] = firstNonWhitespaceIndex; + wrappedTextIndentLengths[i] = wrappedTextIndentLength; + renderLineContents[i] = renderLineContent; + allCharOffsets[i] = tmp[0]; + allVisibleColumns[i] = tmp[1]; + } + containerDomNode.innerHTML = sb.build(); + + containerDomNode.style.position = 'absolute'; + containerDomNode.style.top = '10000'; + containerDomNode.style.wordWrap = 'break-word'; + document.body.appendChild(containerDomNode); + + let range = document.createRange(); + const lineDomNodes = Array.prototype.slice.call(containerDomNode.children, 0); + + let result: (LineBreakData | null)[] = []; + for (let i = 0; i < requests.length; i++) { + const lineDomNode = lineDomNodes[i]; + const breakOffsets: number[] | null = readLineBreaks(range, lineDomNode, renderLineContents[i], allCharOffsets[i]); + if (breakOffsets === null) { + result[i] = null; + continue; + } + + const firstNonWhitespaceIndex = firstNonWhitespaceIndices[i]; + const wrappedTextIndentLength = wrappedTextIndentLengths[i]; + const visibleColumns = allVisibleColumns[i]; + + const breakOffsetsVisibleColumn: number[] = []; + for (let j = 0, len = breakOffsets.length; j < len; j++) { + breakOffsetsVisibleColumn[j] = visibleColumns[breakOffsets[j]]; + } + + if (firstNonWhitespaceIndex !== 0) { + // All break offsets are relative to the renderLineContent, make them absolute again + for (let j = 0, len = breakOffsets.length; j < len; j++) { + breakOffsets[j] += firstNonWhitespaceIndex; + } + } + + result[i] = new LineBreakData(breakOffsets, breakOffsetsVisibleColumn, wrappedTextIndentLength); + } + + document.body.removeChild(containerDomNode); + 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('
    '); + // if (containsRTL) { + // sb.appendASCIIString('" dir="ltr'); + // } + + const len = lineContent.length; + let visibleColumn = initialVisibleColumn; + let charOffset = 0; + let charOffsets: number[] = []; + let visibleColumns: number[] = []; + let nextCharCode = (0 < len ? lineContent.charCodeAt(0) : CharCode.Null); + + sb.appendASCIIString(''); + for (let charIndex = 0; charIndex < len; charIndex++) { + if (charIndex !== 0 && charIndex % Constants.SPAN_MODULO_LIMIT === 0) { + sb.appendASCIIString(''); + } + charOffsets[charIndex] = charOffset; + visibleColumns[charIndex] = visibleColumn; + const charCode = nextCharCode; + nextCharCode = (charIndex + 1 < len ? lineContent.charCodeAt(charIndex + 1) : CharCode.Null); + let producedCharacters = 1; + let charWidth = 1; + switch (charCode) { + case CharCode.Tab: + producedCharacters = (tabSize - (visibleColumn % tabSize)); + charWidth = producedCharacters; + for (let space = 1; space <= producedCharacters; space++) { + if (space < producedCharacters) { + sb.write1(0xA0); //   + } else { + sb.appendASCII(CharCode.Space); + } + } + break; + + case CharCode.Space: + if (nextCharCode === CharCode.Space) { + sb.write1(0xA0); //   + } else { + sb.appendASCII(CharCode.Space); + } + break; + + case CharCode.LessThan: + sb.appendASCIIString('<'); + break; + + case CharCode.GreaterThan: + sb.appendASCIIString('>'); + break; + + case CharCode.Ampersand: + sb.appendASCIIString('&'); + break; + + case CharCode.Null: + sb.appendASCIIString('�'); + break; + + case CharCode.UTF8_BOM: + case CharCode.LINE_SEPARATOR: + case CharCode.PARAGRAPH_SEPARATOR: + case CharCode.NEXT_LINE: + sb.write1(0xFFFD); + break; + + default: + if (strings.isFullWidthCharacter(charCode)) { + charWidth++; + } + // if (renderControlCharacters && charCode < 32) { + // sb.write1(9216 + charCode); + // } else { + sb.write1(charCode); + // } + } + + charOffset += producedCharacters; + visibleColumn += charWidth; + } + sb.appendASCIIString(''); + + charOffsets[lineContent.length] = charOffset; + visibleColumns[lineContent.length] = visibleColumn; + + sb.appendASCIIString('
    '); + + return [charOffsets, visibleColumns]; +} + +function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent: string, charOffsets: number[]): number[] | null { + if (lineContent.length <= 1) { + return null; + } + const spans = Array.prototype.slice.call(lineDomNode.children, 0); + + const breakOffsets: number[] = []; + try { + discoverBreaks(range, spans, charOffsets, 0, null, lineContent.length - 1, null, breakOffsets); + } catch (err) { + console.log(err); + return null; + } + + if (breakOffsets.length === 0) { + return null; + } + + breakOffsets.push(lineContent.length); + return breakOffsets; +} + +type MaybeRects = ClientRectList | DOMRectList | null; + +function discoverBreaks(range: Range, spans: HTMLSpanElement[], charOffsets: number[], low: number, lowRects: MaybeRects, high: number, highRects: MaybeRects, result: number[]): void { + if (low === high) { + return; + } + + lowRects = lowRects || readClientRect(range, spans, charOffsets[low], charOffsets[low + 1]); + highRects = highRects || readClientRect(range, spans, charOffsets[high], charOffsets[high + 1]); + + if (Math.abs(lowRects[0].top - highRects[0].top) <= 0.1) { + // same line + return; + } + + // there is at least one line break between these two offsets + if (low + 1 === high) { + // the two characters are adjacent, so the line break must be exactly between them + result.push(high); + return; + } + + const mid = low + ((high - low) / 2) | 0; + const midRects = readClientRect(range, spans, charOffsets[mid], charOffsets[mid + 1]); + discoverBreaks(range, spans, charOffsets, low, lowRects, mid, midRects, result); + discoverBreaks(range, spans, charOffsets, mid, midRects, high, highRects, result); +} + +function readClientRect(range: Range, spans: HTMLSpanElement[], startOffset: number, endOffset: number): ClientRectList | DOMRectList { + range.setStart(spans[(startOffset / Constants.SPAN_MODULO_LIMIT) | 0].firstChild!, startOffset % Constants.SPAN_MODULO_LIMIT); + range.setEnd(spans[(endOffset / Constants.SPAN_MODULO_LIMIT) | 0].firstChild!, endOffset % Constants.SPAN_MODULO_LIMIT); + return range.getClientRects(); +} diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index 9057fdfbe2252..519a26bb5f853 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { CoreEditorCommand, CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; +import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; import { IEditorMouseEvent, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; -import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; +import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { IConfiguration } from 'vs/editor/common/editorCommon'; @@ -35,67 +35,60 @@ export interface IMouseDispatchData { } export interface ICommandDelegate { - executeEditorCommand(editorCommand: CoreEditorCommand, args: any): void; - - paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null): void; - type(source: string, text: string): void; - replacePreviousChar(source: string, text: string, replaceCharCnt: number): void; - compositionStart(source: string): void; - compositionEnd(source: string): void; - cut(source: string): void; + paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void; + type(text: string): void; + replacePreviousChar(text: string, replaceCharCnt: number): void; + startComposition(): void; + endComposition(): void; + cut(): void; } export class ViewController { private readonly configuration: IConfiguration; private readonly viewModel: IViewModel; - private readonly outgoingEvents: ViewOutgoingEvents; + private readonly userInputEvents: ViewUserInputEvents; private readonly commandDelegate: ICommandDelegate; constructor( configuration: IConfiguration, viewModel: IViewModel, - outgoingEvents: ViewOutgoingEvents, + userInputEvents: ViewUserInputEvents, commandDelegate: ICommandDelegate ) { this.configuration = configuration; this.viewModel = viewModel; - this.outgoingEvents = outgoingEvents; + this.userInputEvents = userInputEvents; this.commandDelegate = commandDelegate; } - private _execMouseCommand(editorCommand: CoreEditorCommand, args: any): void { - args.source = 'mouse'; - this.commandDelegate.executeEditorCommand(editorCommand, args); + public paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void { + this.commandDelegate.paste(text, pasteOnNewLine, multicursorText, mode); } - public paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null): void { - this.commandDelegate.paste(source, text, pasteOnNewLine, multicursorText); + public type(text: string): void { + this.commandDelegate.type(text); } - public type(source: string, text: string): void { - this.commandDelegate.type(source, text); + public replacePreviousChar(text: string, replaceCharCnt: number): void { + this.commandDelegate.replacePreviousChar(text, replaceCharCnt); } - public replacePreviousChar(source: string, text: string, replaceCharCnt: number): void { - this.commandDelegate.replacePreviousChar(source, text, replaceCharCnt); + public compositionStart(): void { + this.commandDelegate.startComposition(); } - public compositionStart(source: string): void { - this.commandDelegate.compositionStart(source); + public compositionEnd(): void { + this.commandDelegate.endComposition(); } - public compositionEnd(source: string): void { - this.commandDelegate.compositionEnd(source); + public cut(): void { + this.commandDelegate.cut(); } - public cut(source: string): void { - this.commandDelegate.cut(source); - } - - public setSelection(source: string, modelSelection: Selection): void { - this.commandDelegate.executeEditorCommand(CoreNavigationCommands.SetSelection, { - source: source, + public setSelection(modelSelection: Selection): void { + CoreNavigationCommands.SetSelection.runCoreEditorCommand(this.viewModel, { + source: 'keyboard', selection: modelSelection }); } @@ -116,8 +109,9 @@ export class ViewController { return data.ctrlKey; case 'metaKey': return data.metaKey; + default: + return false; } - return false; } private _hasNonMulticursorModifier(data: IMouseDispatchData): boolean { @@ -128,12 +122,15 @@ export class ViewController { return data.altKey || data.metaKey; case 'metaKey': return data.ctrlKey || data.altKey; + default: + return false; } - return false; } public dispatchMouse(data: IMouseDispatchData): void { - const selectionClipboardIsOn = (platform.isLinux && this.configuration.options.get(EditorOption.selectionClipboard)); + const options = this.configuration.options; + const selectionClipboardIsOn = (platform.isLinux && options.get(EditorOption.selectionClipboard)); + const columnSelection = options.get(EditorOption.columnSelection); if (data.middleButton && !selectionClipboardIsOn) { this._columnSelect(data.position, data.mouseColumn, data.inSelectionMode); } else if (data.startedOnLineNumbers) { @@ -196,7 +193,11 @@ export class ViewController { if (data.altKey) { this._columnSelect(data.position, data.mouseColumn, true); } else { - this._moveToSelect(data.position); + if (columnSelection) { + this._columnSelect(data.position, data.mouseColumn, true); + } else { + this._moveToSelect(data.position); + } } } else { this.moveTo(data.position); @@ -208,22 +209,24 @@ export class ViewController { private _usualArgs(viewPosition: Position) { viewPosition = this._validateViewColumn(viewPosition); return { + source: 'mouse', position: this._convertViewToModelPosition(viewPosition), viewPosition: viewPosition }; } public moveTo(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.MoveTo, this._usualArgs(viewPosition)); + CoreNavigationCommands.MoveTo.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _moveToSelect(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.MoveToSelect, this._usualArgs(viewPosition)); + CoreNavigationCommands.MoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _columnSelect(viewPosition: Position, mouseColumn: number, doColumnSelect: boolean): void { viewPosition = this._validateViewColumn(viewPosition); - this._execMouseCommand(CoreNavigationCommands.ColumnSelect, { + CoreNavigationCommands.ColumnSelect.runCoreEditorCommand(this.viewModel, { + source: 'mouse', position: this._convertViewToModelPosition(viewPosition), viewPosition: viewPosition, mouseColumn: mouseColumn, @@ -233,7 +236,8 @@ export class ViewController { private _createCursor(viewPosition: Position, wholeLine: boolean): void { viewPosition = this._validateViewColumn(viewPosition); - this._execMouseCommand(CoreNavigationCommands.CreateCursor, { + CoreNavigationCommands.CreateCursor.runCoreEditorCommand(this.viewModel, { + source: 'mouse', position: this._convertViewToModelPosition(viewPosition), viewPosition: viewPosition, wholeLine: wholeLine @@ -241,39 +245,39 @@ export class ViewController { } private _lastCursorMoveToSelect(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.LastCursorMoveToSelect, this._usualArgs(viewPosition)); + CoreNavigationCommands.LastCursorMoveToSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _wordSelect(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.WordSelect, this._usualArgs(viewPosition)); + CoreNavigationCommands.WordSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _wordSelectDrag(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.WordSelectDrag, this._usualArgs(viewPosition)); + CoreNavigationCommands.WordSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _lastCursorWordSelect(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.LastCursorWordSelect, this._usualArgs(viewPosition)); + CoreNavigationCommands.LastCursorWordSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _lineSelect(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.LineSelect, this._usualArgs(viewPosition)); + CoreNavigationCommands.LineSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _lineSelectDrag(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.LineSelectDrag, this._usualArgs(viewPosition)); + CoreNavigationCommands.LineSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _lastCursorLineSelect(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.LastCursorLineSelect, this._usualArgs(viewPosition)); + CoreNavigationCommands.LastCursorLineSelect.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _lastCursorLineSelectDrag(viewPosition: Position): void { - this._execMouseCommand(CoreNavigationCommands.LastCursorLineSelectDrag, this._usualArgs(viewPosition)); + CoreNavigationCommands.LastCursorLineSelectDrag.runCoreEditorCommand(this.viewModel, this._usualArgs(viewPosition)); } private _selectAll(): void { - this._execMouseCommand(CoreNavigationCommands.SelectAll, {}); + CoreNavigationCommands.SelectAll.runCoreEditorCommand(this.viewModel, { source: 'mouse' }); } // ---------------------- @@ -283,42 +287,42 @@ export class ViewController { } public emitKeyDown(e: IKeyboardEvent): void { - this.outgoingEvents.emitKeyDown(e); + this.userInputEvents.emitKeyDown(e); } public emitKeyUp(e: IKeyboardEvent): void { - this.outgoingEvents.emitKeyUp(e); + this.userInputEvents.emitKeyUp(e); } public emitContextMenu(e: IEditorMouseEvent): void { - this.outgoingEvents.emitContextMenu(e); + this.userInputEvents.emitContextMenu(e); } public emitMouseMove(e: IEditorMouseEvent): void { - this.outgoingEvents.emitMouseMove(e); + this.userInputEvents.emitMouseMove(e); } public emitMouseLeave(e: IPartialEditorMouseEvent): void { - this.outgoingEvents.emitMouseLeave(e); + this.userInputEvents.emitMouseLeave(e); } public emitMouseUp(e: IEditorMouseEvent): void { - this.outgoingEvents.emitMouseUp(e); + this.userInputEvents.emitMouseUp(e); } public emitMouseDown(e: IEditorMouseEvent): void { - this.outgoingEvents.emitMouseDown(e); + this.userInputEvents.emitMouseDown(e); } public emitMouseDrag(e: IEditorMouseEvent): void { - this.outgoingEvents.emitMouseDrag(e); + this.userInputEvents.emitMouseDrag(e); } public emitMouseDrop(e: IPartialEditorMouseEvent): void { - this.outgoingEvents.emitMouseDrop(e); + this.userInputEvents.emitMouseDrop(e); } public emitMouseWheel(e: IMouseWheelEvent): void { - this.outgoingEvents.emitMouseWheel(e); + this.userInputEvents.emitMouseWheel(e); } } diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 15f286d6abe49..b656a3f8ec6f0 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { Selection } from 'vs/editor/common/core/selection'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -11,9 +12,9 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler'; import { ITextAreaHandlerHelper, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { IContentWidget, IContentWidgetPosition, IOverlayWidget, IOverlayWidgetPosition, IMouseTarget, IViewZoneChangeAccessor, IEditorAriaOptions } from 'vs/editor/browser/editorBrowser'; import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController'; -import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; +import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents'; import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/contentWidgets'; @@ -36,59 +37,53 @@ import { ScrollDecorationViewPart } from 'vs/editor/browser/viewParts/scrollDeco import { SelectionsOverlay } from 'vs/editor/browser/viewParts/selections/selections'; import { ViewCursors } from 'vs/editor/browser/viewParts/viewCursors/viewCursors'; import { ViewZones } from 'vs/editor/browser/viewParts/viewZones/viewZones'; -import { Cursor } from 'vs/editor/common/controller/cursor'; import { Position } from 'vs/editor/common/core/position'; -import { IConfiguration } from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; -import { ViewEventDispatcher } from 'vs/editor/common/view/viewEventDispatcher'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { IThemeService, getThemeTypeSelector } from 'vs/platform/theme/common/themeService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; export interface IContentWidgetData { - widget: editorBrowser.IContentWidget; - position: editorBrowser.IContentWidgetPosition | null; + widget: IContentWidget; + position: IContentWidgetPosition | null; } export interface IOverlayWidgetData { - widget: editorBrowser.IOverlayWidget; - position: editorBrowser.IOverlayWidgetPosition | null; + widget: IOverlayWidget; + position: IOverlayWidgetPosition | null; } -const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; - export class View extends ViewEventHandler { - private readonly eventDispatcher: ViewEventDispatcher; - - private _scrollbar: EditorScrollbar; + private readonly _scrollbar: EditorScrollbar; private readonly _context: ViewContext; - private readonly _cursor: Cursor; + private _selections: Selection[]; // The view lines - private viewLines: ViewLines; + private readonly _viewLines: ViewLines; // These are parts, but we must do some API related calls on them, so we keep a reference - private viewZones: ViewZones; - private contentWidgets: ViewContentWidgets; - private overlayWidgets: ViewOverlayWidgets; - private viewCursors: ViewCursors; - private viewParts: ViewPart[]; + private readonly _viewZones: ViewZones; + private readonly _contentWidgets: ViewContentWidgets; + private readonly _overlayWidgets: ViewOverlayWidgets; + private readonly _viewCursors: ViewCursors; + private readonly _viewParts: ViewPart[]; private readonly _textAreaHandler: TextAreaHandler; - private readonly pointerHandler: PointerHandler; - - private readonly outgoingEvents: ViewOutgoingEvents; + private readonly _pointerHandler: PointerHandler; // Dom nodes - private linesContent: FastDomNode; - public domNode: FastDomNode; - private overflowGuardContainer: FastDomNode; + private readonly _linesContent: FastDomNode; + public readonly domNode: FastDomNode; + private readonly _overflowGuardContainer: FastDomNode; // Actual mutable state private _renderAnimationFrame: IDisposable | null; @@ -98,76 +93,74 @@ export class View extends ViewEventHandler { configuration: IConfiguration, themeService: IThemeService, model: IViewModel, - cursor: Cursor, - outgoingEvents: ViewOutgoingEvents + userInputEvents: ViewUserInputEvents, + overflowWidgetsDomNode: HTMLElement | undefined ) { super(); - this._cursor = cursor; + this._selections = [new Selection(1, 1, 1, 1)]; this._renderAnimationFrame = null; - this.outgoingEvents = outgoingEvents; - const viewController = new ViewController(configuration, model, this.outgoingEvents, commandDelegate); + const viewController = new ViewController(configuration, model, userInputEvents, commandDelegate); - // The event dispatcher will always go through _renderOnce before dispatching any events - this.eventDispatcher = new ViewEventDispatcher((callback: () => void) => this._renderOnce(callback)); + // The view context is passed on to most classes (basically to reduce param. counts in ctors) + this._context = new ViewContext(configuration, themeService.getColorTheme(), model); // Ensure the view is the first event handler in order to update the layout - this.eventDispatcher.addEventHandler(this); - - // The view context is passed on to most classes (basically to reduce param. counts in ctors) - this._context = new ViewContext(configuration, themeService.getTheme(), model, this.eventDispatcher); + this._context.addEventHandler(this); - this._register(themeService.onThemeChange(theme => { - this._context.theme = theme; - this.eventDispatcher.emit(new viewEvents.ViewThemeChangedEvent()); + this._register(themeService.onDidColorThemeChange(theme => { + this._context.theme.update(theme); + this._context.model.onDidColorThemeChange(); this.render(true, false); })); - this.viewParts = []; + this._viewParts = []; // Keyboard handler - this._textAreaHandler = new TextAreaHandler(this._context, viewController, this.createTextAreaHandlerHelper()); - this.viewParts.push(this._textAreaHandler); + this._textAreaHandler = new TextAreaHandler(this._context, viewController, this._createTextAreaHandlerHelper()); + this._viewParts.push(this._textAreaHandler); // These two dom nodes must be constructed up front, since references are needed in the layout provider (scrolling & co.) - this.linesContent = createFastDomNode(document.createElement('div')); - this.linesContent.setClassName('lines-content' + ' monaco-editor-background'); - this.linesContent.setPosition('absolute'); + this._linesContent = createFastDomNode(document.createElement('div')); + this._linesContent.setClassName('lines-content' + ' monaco-editor-background'); + this._linesContent.setPosition('absolute'); this.domNode = createFastDomNode(document.createElement('div')); - this.domNode.setClassName(this.getEditorClassName()); + this.domNode.setClassName(this._getEditorClassName()); + // Set role 'code' for better screen reader support https://github.com/microsoft/vscode/issues/93438 + this.domNode.setAttribute('role', 'code'); - this.overflowGuardContainer = createFastDomNode(document.createElement('div')); - PartFingerprints.write(this.overflowGuardContainer, PartFingerprint.OverflowGuard); - this.overflowGuardContainer.setClassName('overflow-guard'); + this._overflowGuardContainer = createFastDomNode(document.createElement('div')); + PartFingerprints.write(this._overflowGuardContainer, PartFingerprint.OverflowGuard); + this._overflowGuardContainer.setClassName('overflow-guard'); - this._scrollbar = new EditorScrollbar(this._context, this.linesContent, this.domNode, this.overflowGuardContainer); - this.viewParts.push(this._scrollbar); + this._scrollbar = new EditorScrollbar(this._context, this._linesContent, this.domNode, this._overflowGuardContainer); + this._viewParts.push(this._scrollbar); // View Lines - this.viewLines = new ViewLines(this._context, this.linesContent); + this._viewLines = new ViewLines(this._context, this._linesContent); // View Zones - this.viewZones = new ViewZones(this._context); - this.viewParts.push(this.viewZones); + this._viewZones = new ViewZones(this._context); + this._viewParts.push(this._viewZones); // Decorations overview ruler const decorationsOverviewRuler = new DecorationsOverviewRuler(this._context); - this.viewParts.push(decorationsOverviewRuler); + this._viewParts.push(decorationsOverviewRuler); const scrollDecoration = new ScrollDecorationViewPart(this._context); - this.viewParts.push(scrollDecoration); + this._viewParts.push(scrollDecoration); const contentViewOverlays = new ContentViewOverlays(this._context); - this.viewParts.push(contentViewOverlays); + this._viewParts.push(contentViewOverlays); contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new IndentGuidesOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context)); const marginViewOverlays = new MarginViewOverlays(this._context); - this.viewParts.push(marginViewOverlays); + this._viewParts.push(marginViewOverlays); marginViewOverlays.addDynamicOverlay(new CurrentLineMarginHighlightOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context)); @@ -175,26 +168,26 @@ export class View extends ViewEventHandler { marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context)); const margin = new Margin(this._context); - margin.getDomNode().appendChild(this.viewZones.marginDomNode); + margin.getDomNode().appendChild(this._viewZones.marginDomNode); margin.getDomNode().appendChild(marginViewOverlays.getDomNode()); - this.viewParts.push(margin); + this._viewParts.push(margin); // Content widgets - this.contentWidgets = new ViewContentWidgets(this._context, this.domNode); - this.viewParts.push(this.contentWidgets); + this._contentWidgets = new ViewContentWidgets(this._context, this.domNode); + this._viewParts.push(this._contentWidgets); - this.viewCursors = new ViewCursors(this._context); - this.viewParts.push(this.viewCursors); + this._viewCursors = new ViewCursors(this._context); + this._viewParts.push(this._viewCursors); // Overlay widgets - this.overlayWidgets = new ViewOverlayWidgets(this._context); - this.viewParts.push(this.overlayWidgets); + this._overlayWidgets = new ViewOverlayWidgets(this._context); + this._viewParts.push(this._overlayWidgets); const rulers = new Rulers(this._context); - this.viewParts.push(rulers); + this._viewParts.push(rulers); const minimap = new Minimap(this._context); - this.viewParts.push(minimap); + this._viewParts.push(minimap); // -------------- Wire dom nodes up @@ -203,80 +196,79 @@ export class View extends ViewEventHandler { overviewRulerData.parent.insertBefore(decorationsOverviewRuler.getDomNode(), overviewRulerData.insertBefore); } - this.linesContent.appendChild(contentViewOverlays.getDomNode()); - this.linesContent.appendChild(rulers.domNode); - this.linesContent.appendChild(this.viewZones.domNode); - this.linesContent.appendChild(this.viewLines.getDomNode()); - this.linesContent.appendChild(this.contentWidgets.domNode); - this.linesContent.appendChild(this.viewCursors.getDomNode()); - this.overflowGuardContainer.appendChild(margin.getDomNode()); - this.overflowGuardContainer.appendChild(this._scrollbar.getDomNode()); - this.overflowGuardContainer.appendChild(scrollDecoration.getDomNode()); - this.overflowGuardContainer.appendChild(this._textAreaHandler.textArea); - this.overflowGuardContainer.appendChild(this._textAreaHandler.textAreaCover); - this.overflowGuardContainer.appendChild(this.overlayWidgets.getDomNode()); - this.overflowGuardContainer.appendChild(minimap.getDomNode()); - this.domNode.appendChild(this.overflowGuardContainer); - this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode); + this._linesContent.appendChild(contentViewOverlays.getDomNode()); + this._linesContent.appendChild(rulers.domNode); + this._linesContent.appendChild(this._viewZones.domNode); + this._linesContent.appendChild(this._viewLines.getDomNode()); + this._linesContent.appendChild(this._contentWidgets.domNode); + this._linesContent.appendChild(this._viewCursors.getDomNode()); + this._overflowGuardContainer.appendChild(margin.getDomNode()); + this._overflowGuardContainer.appendChild(this._scrollbar.getDomNode()); + this._overflowGuardContainer.appendChild(scrollDecoration.getDomNode()); + this._overflowGuardContainer.appendChild(this._textAreaHandler.textArea); + this._overflowGuardContainer.appendChild(this._textAreaHandler.textAreaCover); + this._overflowGuardContainer.appendChild(this._overlayWidgets.getDomNode()); + this._overflowGuardContainer.appendChild(minimap.getDomNode()); + this.domNode.appendChild(this._overflowGuardContainer); + + if (overflowWidgetsDomNode) { + overflowWidgetsDomNode.appendChild(this._contentWidgets.overflowingContentWidgetsDomNode.domNode); + } else { + this.domNode.appendChild(this._contentWidgets.overflowingContentWidgetsDomNode); + } this._applyLayout(); // Pointer handler - this.pointerHandler = this._register(new PointerHandler(this._context, viewController, this.createPointerHandlerHelper())); - - this._register(model.addEventListener((events: viewEvents.ViewEvent[]) => { - this.eventDispatcher.emitMany(events); - })); - - this._register(this._cursor.addEventListener((events: viewEvents.ViewEvent[]) => { - this.eventDispatcher.emitMany(events); - })); + this._pointerHandler = this._register(new PointerHandler(this._context, viewController, this._createPointerHandlerHelper())); } private _flushAccumulatedAndRenderNow(): void { this._renderNow(); } - private createPointerHandlerHelper(): IPointerHandlerHelper { + private _createPointerHandlerHelper(): IPointerHandlerHelper { return { viewDomNode: this.domNode.domNode, - linesContentDomNode: this.linesContent.domNode, + linesContentDomNode: this._linesContent.domNode, focusTextArea: () => { this.focus(); }, - getLastViewCursorsRenderData: () => { - return this.viewCursors.getLastRenderData() || []; + getLastRenderData: (): PointerHandlerLastRenderData => { + const lastViewCursorsRenderData = this._viewCursors.getLastRenderData() || []; + const lastTextareaPosition = this._textAreaHandler.getLastRenderData(); + return new PointerHandlerLastRenderData(lastViewCursorsRenderData, lastTextareaPosition); }, shouldSuppressMouseDownOnViewZone: (viewZoneId: string) => { - return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId); + return this._viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId); }, shouldSuppressMouseDownOnWidget: (widgetId: string) => { - return this.contentWidgets.shouldSuppressMouseDownOnWidget(widgetId); + return this._contentWidgets.shouldSuppressMouseDownOnWidget(widgetId); }, getPositionFromDOMInfo: (spanNode: HTMLElement, offset: number) => { this._flushAccumulatedAndRenderNow(); - return this.viewLines.getPositionFromDOMInfo(spanNode, offset); + return this._viewLines.getPositionFromDOMInfo(spanNode, offset); }, visibleRangeForPosition: (lineNumber: number, column: number) => { this._flushAccumulatedAndRenderNow(); - return this.viewLines.visibleRangeForPosition(new Position(lineNumber, column)); + return this._viewLines.visibleRangeForPosition(new Position(lineNumber, column)); }, getLineWidth: (lineNumber: number) => { this._flushAccumulatedAndRenderNow(); - return this.viewLines.getLineWidth(lineNumber); + return this._viewLines.getLineWidth(lineNumber); } }; } - private createTextAreaHandlerHelper(): ITextAreaHandlerHelper { + private _createTextAreaHandlerHelper(): ITextAreaHandlerHelper { return { visibleRangeForPositionRelativeToEditor: (lineNumber: number, column: number) => { this._flushAccumulatedAndRenderNow(); - return this.viewLines.visibleRangeForPosition(new Position(lineNumber, column)); + return this._viewLines.visibleRangeForPosition(new Position(lineNumber, column)); } }; } @@ -288,41 +280,38 @@ export class View extends ViewEventHandler { this.domNode.setWidth(layoutInfo.width); this.domNode.setHeight(layoutInfo.height); - this.overflowGuardContainer.setWidth(layoutInfo.width); - this.overflowGuardContainer.setHeight(layoutInfo.height); + this._overflowGuardContainer.setWidth(layoutInfo.width); + this._overflowGuardContainer.setHeight(layoutInfo.height); - this.linesContent.setWidth(1000000); - this.linesContent.setHeight(1000000); + this._linesContent.setWidth(1000000); + this._linesContent.setHeight(1000000); } - private getEditorClassName() { + private _getEditorClassName() { const focused = this._textAreaHandler.isFocused() ? ' focused' : ''; return this._context.configuration.options.get(EditorOption.editorClassName) + ' ' + getThemeTypeSelector(this._context.theme.type) + focused; } // --- begin event handlers - + public handleEvents(events: viewEvents.ViewEvent[]): void { + super.handleEvents(events); + this._scheduleRender(); + } public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - this.domNode.setClassName(this.getEditorClassName()); + this.domNode.setClassName(this._getEditorClassName()); this._applyLayout(); return false; } - public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { - this.domNode.setClassName(this.getEditorClassName()); - this._context.model.setHasFocus(e.isFocused); - if (e.isFocused) { - this.outgoingEvents.emitViewFocusGained(); - } else { - this.outgoingEvents.emitViewFocusLost(); - } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + this._selections = e.selections; return false; } - public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { - this.outgoingEvents.emitScrollChanged(e); + public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { + this.domNode.setClassName(this._getEditorClassName()); return false; } public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { - this.domNode.setClassName(this.getEditorClassName()); + this.domNode.setClassName(this._getEditorClassName()); return false; } @@ -334,26 +323,20 @@ export class View extends ViewEventHandler { this._renderAnimationFrame = null; } - this.eventDispatcher.removeEventHandler(this); - this.outgoingEvents.dispose(); + this._contentWidgets.overflowingContentWidgetsDomNode.domNode.remove(); + + this._context.removeEventHandler(this); - this.viewLines.dispose(); + this._viewLines.dispose(); // Destroy view parts - for (let i = 0, len = this.viewParts.length; i < len; i++) { - this.viewParts[i].dispose(); + for (let i = 0, len = this._viewParts.length; i < len; i++) { + this._viewParts[i].dispose(); } - this.viewParts = []; super.dispose(); } - private _renderOnce(callback: () => any): any { - const r = safeInvokeNoArg(callback); - this._scheduleRender(); - return r; - } - private _scheduleRender(): void { if (this._renderAnimationFrame === null) { this._renderAnimationFrame = dom.runAtThisOrScheduleAtNextAnimationFrame(this._onRenderScheduled.bind(this), 100); @@ -371,8 +354,8 @@ export class View extends ViewEventHandler { private _getViewPartsToRender(): ViewPart[] { let result: ViewPart[] = [], resultLen = 0; - for (let i = 0, len = this.viewParts.length; i < len; i++) { - const viewPart = this.viewParts[i]; + for (let i = 0, len = this._viewParts.length; i < len; i++) { + const viewPart = this._viewParts[i]; if (viewPart.shouldRender()) { result[resultLen++] = viewPart; } @@ -387,7 +370,7 @@ export class View extends ViewEventHandler { let viewPartsToRender = this._getViewPartsToRender(); - if (!this.viewLines.shouldRender() && viewPartsToRender.length === 0) { + if (!this._viewLines.shouldRender() && viewPartsToRender.length === 0) { // Nothing to render return; } @@ -396,26 +379,26 @@ export class View extends ViewEventHandler { this._context.model.setViewport(partialViewportData.startLineNumber, partialViewportData.endLineNumber, partialViewportData.centeredLineNumber); const viewportData = new ViewportData( - this._cursor.getViewSelections(), + this._selections, partialViewportData, this._context.viewLayout.getWhitespaceViewportData(), this._context.model ); - if (this.contentWidgets.shouldRender()) { + if (this._contentWidgets.shouldRender()) { // Give the content widgets a chance to set their max width before a possible synchronous layout - this.contentWidgets.onBeforeRender(viewportData); + this._contentWidgets.onBeforeRender(viewportData); } - if (this.viewLines.shouldRender()) { - this.viewLines.renderText(viewportData); - this.viewLines.onDidRender(); + if (this._viewLines.shouldRender()) { + this._viewLines.renderText(viewportData); + this._viewLines.onDidRender(); // Rendering of viewLines might cause scroll events to occur, so collect view parts to render again viewPartsToRender = this._getViewPartsToRender(); } - const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this.viewLines); + const renderingContext = new RenderingContext(this._context.viewLayout, viewportData, this._viewLines); // Render the rest of the parts for (let i = 0, len = viewPartsToRender.length; i < len; i++) { @@ -437,11 +420,11 @@ export class View extends ViewEventHandler { } public restoreState(scrollPosition: { scrollLeft: number; scrollTop: number; }): void { - this._context.viewLayout.setScrollPositionNow({ scrollTop: scrollPosition.scrollTop }); + this._context.model.setScrollPosition({ scrollTop: scrollPosition.scrollTop }, ScrollType.Immediate); this._context.model.tokenizeViewport(); this._renderNow(); - this.viewLines.updateLineWidths(); - this._context.viewLayout.setScrollPositionNow({ scrollLeft: scrollPosition.scrollLeft }); + this._viewLines.updateLineWidths(); + this._context.model.setScrollPosition({ scrollLeft: scrollPosition.scrollLeft }, ScrollType.Immediate); } public getOffsetForColumn(modelLineNumber: number, modelColumn: number): number { @@ -451,65 +434,36 @@ export class View extends ViewEventHandler { }); const viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(modelPosition); this._flushAccumulatedAndRenderNow(); - const visibleRange = this.viewLines.visibleRangeForPosition(new Position(viewPosition.lineNumber, viewPosition.column)); + const visibleRange = this._viewLines.visibleRangeForPosition(new Position(viewPosition.lineNumber, viewPosition.column)); if (!visibleRange) { return -1; } return visibleRange.left; } - public getTargetAtClientPoint(clientX: number, clientY: number): editorBrowser.IMouseTarget | null { - return this.pointerHandler.getTargetAtClientPoint(clientX, clientY); + public getTargetAtClientPoint(clientX: number, clientY: number): IMouseTarget | null { + const mouseTarget = this._pointerHandler.getTargetAtClientPoint(clientX, clientY); + if (!mouseTarget) { + return null; + } + return ViewUserInputEvents.convertViewToModelMouseTarget(mouseTarget, this._context.model.coordinatesConverter); } public createOverviewRuler(cssClassName: string): OverviewRuler { return new OverviewRuler(this._context, cssClassName); } - public change(callback: (changeAccessor: editorBrowser.IViewZoneChangeAccessor) => any): boolean { - let zonesHaveChanged = false; - - this._renderOnce(() => { - const changeAccessor: editorBrowser.IViewZoneChangeAccessor = { - addZone: (zone: editorBrowser.IViewZone): string => { - zonesHaveChanged = true; - return this.viewZones.addZone(zone); - }, - removeZone: (id: string): void => { - if (!id) { - return; - } - zonesHaveChanged = this.viewZones.removeZone(id) || zonesHaveChanged; - }, - layoutZone: (id: string): void => { - if (!id) { - return; - } - zonesHaveChanged = this.viewZones.layoutZone(id) || zonesHaveChanged; - } - }; - - safeInvoke1Arg(callback, changeAccessor); - - // Invalidate changeAccessor - changeAccessor.addZone = invalidFunc; - changeAccessor.removeZone = invalidFunc; - changeAccessor.layoutZone = invalidFunc; - - if (zonesHaveChanged) { - this._context.viewLayout.onHeightMaybeChanged(); - this._context.privateViewEventBus.emit(new viewEvents.ViewZonesChangedEvent()); - } - }); - return zonesHaveChanged; + public change(callback: (changeAccessor: IViewZoneChangeAccessor) => any): void { + this._viewZones.changeViewZones(callback); + this._scheduleRender(); } public render(now: boolean, everything: boolean): void { if (everything) { // Force everything to render... - this.viewLines.forceShouldRender(); - for (let i = 0, len = this.viewParts.length; i < len; i++) { - const viewPart = this.viewParts[i]; + this._viewLines.forceShouldRender(); + for (let i = 0, len = this._viewParts.length; i < len; i++) { + const viewPart = this._viewParts[i]; viewPart.forceShouldRender(); } } @@ -528,41 +482,54 @@ export class View extends ViewEventHandler { return this._textAreaHandler.isFocused(); } + public refreshFocusState() { + this._textAreaHandler.refreshFocusState(); + } + + public setAriaOptions(options: IEditorAriaOptions): void { + this._textAreaHandler.setAriaOptions(options); + } + public addContentWidget(widgetData: IContentWidgetData): void { - this.contentWidgets.addWidget(widgetData.widget); + this._contentWidgets.addWidget(widgetData.widget); this.layoutContentWidget(widgetData); this._scheduleRender(); } public layoutContentWidget(widgetData: IContentWidgetData): void { - const newPosition = widgetData.position ? widgetData.position.position : null; - const newRange = widgetData.position ? widgetData.position.range || null : null; + let newRange = widgetData.position ? widgetData.position.range || null : null; + if (newRange === null) { + const newPosition = widgetData.position ? widgetData.position.position : null; + if (newPosition !== null) { + newRange = new Range(newPosition.lineNumber, newPosition.column, newPosition.lineNumber, newPosition.column); + } + } const newPreference = widgetData.position ? widgetData.position.preference : null; - this.contentWidgets.setWidgetPosition(widgetData.widget, newPosition, newRange, newPreference); + this._contentWidgets.setWidgetPosition(widgetData.widget, newRange, newPreference); this._scheduleRender(); } public removeContentWidget(widgetData: IContentWidgetData): void { - this.contentWidgets.removeWidget(widgetData.widget); + this._contentWidgets.removeWidget(widgetData.widget); this._scheduleRender(); } public addOverlayWidget(widgetData: IOverlayWidgetData): void { - this.overlayWidgets.addWidget(widgetData.widget); + this._overlayWidgets.addWidget(widgetData.widget); this.layoutOverlayWidget(widgetData); this._scheduleRender(); } public layoutOverlayWidget(widgetData: IOverlayWidgetData): void { const newPreference = widgetData.position ? widgetData.position.preference : null; - const shouldRender = this.overlayWidgets.setWidgetPosition(widgetData.widget, newPreference); + const shouldRender = this._overlayWidgets.setWidgetPosition(widgetData.widget, newPreference); if (shouldRender) { this._scheduleRender(); } } public removeOverlayWidget(widgetData: IOverlayWidgetData): void { - this.overlayWidgets.removeWidget(widgetData.widget); + this._overlayWidgets.removeWidget(widgetData.widget); this._scheduleRender(); } @@ -577,11 +544,3 @@ function safeInvokeNoArg(func: Function): any { onUnexpectedError(e); } } - -function safeInvoke1Arg(func: Function, arg1: any): any { - try { - return func(arg1); - } catch (e) { - onUnexpectedError(e); - } -} diff --git a/src/vs/editor/browser/view/viewOutgoingEvents.ts b/src/vs/editor/browser/view/viewOutgoingEvents.ts deleted file mode 100644 index dce981872952d..0000000000000 --- a/src/vs/editor/browser/view/viewOutgoingEvents.ts +++ /dev/null @@ -1,175 +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 { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { MouseTarget } from 'vs/editor/browser/controller/mouseTarget'; -import { IEditorMouseEvent, IMouseTarget, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; -import { IScrollEvent } from 'vs/editor/common/editorCommon'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; - -export interface EventCallback { - (event: T): void; -} - -export class ViewOutgoingEvents extends Disposable { - - public onDidScroll: EventCallback | null = null; - public onDidGainFocus: EventCallback | null = null; - public onDidLoseFocus: EventCallback | null = null; - public onKeyDown: EventCallback | null = null; - public onKeyUp: EventCallback | null = null; - public onContextMenu: EventCallback | null = null; - public onMouseMove: EventCallback | null = null; - public onMouseLeave: EventCallback | null = null; - public onMouseUp: EventCallback | null = null; - public onMouseDown: EventCallback | null = null; - public onMouseDrag: EventCallback | null = null; - public onMouseDrop: EventCallback | null = null; - public onMouseWheel: EventCallback | null = null; - - private readonly _viewModel: IViewModel; - - constructor(viewModel: IViewModel) { - super(); - this._viewModel = viewModel; - } - - public emitScrollChanged(e: viewEvents.ViewScrollChangedEvent): void { - if (this.onDidScroll) { - this.onDidScroll(e); - } - } - - public emitViewFocusGained(): void { - if (this.onDidGainFocus) { - this.onDidGainFocus(undefined); - } - } - - public emitViewFocusLost(): void { - if (this.onDidLoseFocus) { - this.onDidLoseFocus(undefined); - } - } - - public emitKeyDown(e: IKeyboardEvent): void { - if (this.onKeyDown) { - this.onKeyDown(e); - } - } - - public emitKeyUp(e: IKeyboardEvent): void { - if (this.onKeyUp) { - this.onKeyUp(e); - } - } - - public emitContextMenu(e: IEditorMouseEvent): void { - if (this.onContextMenu) { - this.onContextMenu(this._convertViewToModelMouseEvent(e)); - } - } - - public emitMouseMove(e: IEditorMouseEvent): void { - if (this.onMouseMove) { - this.onMouseMove(this._convertViewToModelMouseEvent(e)); - } - } - - public emitMouseLeave(e: IPartialEditorMouseEvent): void { - if (this.onMouseLeave) { - this.onMouseLeave(this._convertViewToModelMouseEvent(e)); - } - } - - public emitMouseUp(e: IEditorMouseEvent): void { - if (this.onMouseUp) { - this.onMouseUp(this._convertViewToModelMouseEvent(e)); - } - } - - public emitMouseDown(e: IEditorMouseEvent): void { - if (this.onMouseDown) { - this.onMouseDown(this._convertViewToModelMouseEvent(e)); - } - } - - public emitMouseDrag(e: IEditorMouseEvent): void { - if (this.onMouseDrag) { - this.onMouseDrag(this._convertViewToModelMouseEvent(e)); - } - } - - public emitMouseDrop(e: IPartialEditorMouseEvent): void { - if (this.onMouseDrop) { - this.onMouseDrop(this._convertViewToModelMouseEvent(e)); - } - } - - public emitMouseWheel(e: IMouseWheelEvent): void { - if (this.onMouseWheel) { - this.onMouseWheel(e); - } - } - - private _convertViewToModelMouseEvent(e: IEditorMouseEvent): IEditorMouseEvent; - private _convertViewToModelMouseEvent(e: IPartialEditorMouseEvent): IPartialEditorMouseEvent; - private _convertViewToModelMouseEvent(e: IEditorMouseEvent | IPartialEditorMouseEvent): IEditorMouseEvent | IPartialEditorMouseEvent { - if (e.target) { - return { - event: e.event, - target: this._convertViewToModelMouseTarget(e.target) - }; - } - return e; - } - - private _convertViewToModelMouseTarget(target: IMouseTarget): IMouseTarget { - return new ExternalMouseTarget( - target.element, - target.type, - target.mouseColumn, - target.position ? this._convertViewToModelPosition(target.position) : null, - target.range ? this._convertViewToModelRange(target.range) : null, - target.detail - ); - } - - private _convertViewToModelPosition(viewPosition: Position): Position { - return this._viewModel.coordinatesConverter.convertViewPositionToModelPosition(viewPosition); - } - - private _convertViewToModelRange(viewRange: Range): Range { - return this._viewModel.coordinatesConverter.convertViewRangeToModelRange(viewRange); - } -} - -class ExternalMouseTarget implements IMouseTarget { - - public readonly element: Element | null; - public readonly type: MouseTargetType; - public readonly mouseColumn: number; - public readonly position: Position | null; - public readonly range: Range | null; - public readonly detail: any; - - constructor(element: Element | null, type: MouseTargetType, mouseColumn: number, position: Position | null, range: Range | null, detail: any) { - this.element = element; - this.type = type; - this.mouseColumn = mouseColumn; - this.position = position; - this.range = range; - this.detail = detail; - } - - public toString(): string { - return MouseTarget.toString(this); - } -} diff --git a/src/vs/editor/browser/view/viewUserInputEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts new file mode 100644 index 0000000000000..22906bda60e29 --- /dev/null +++ b/src/vs/editor/browser/view/viewUserInputEvents.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { MouseTarget } from 'vs/editor/browser/controller/mouseTarget'; +import { IEditorMouseEvent, IMouseTarget, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel'; +import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; + +export interface EventCallback { + (event: T): void; +} + +export class ViewUserInputEvents { + + public onKeyDown: EventCallback | null = null; + public onKeyUp: EventCallback | null = null; + public onContextMenu: EventCallback | null = null; + public onMouseMove: EventCallback | null = null; + public onMouseLeave: EventCallback | null = null; + public onMouseDown: EventCallback | null = null; + public onMouseUp: EventCallback | null = null; + public onMouseDrag: EventCallback | null = null; + public onMouseDrop: EventCallback | null = null; + public onMouseWheel: EventCallback | null = null; + + private readonly _coordinatesConverter: ICoordinatesConverter; + + constructor(coordinatesConverter: ICoordinatesConverter) { + this._coordinatesConverter = coordinatesConverter; + } + + public emitKeyDown(e: IKeyboardEvent): void { + if (this.onKeyDown) { + this.onKeyDown(e); + } + } + + public emitKeyUp(e: IKeyboardEvent): void { + if (this.onKeyUp) { + this.onKeyUp(e); + } + } + + public emitContextMenu(e: IEditorMouseEvent): void { + if (this.onContextMenu) { + this.onContextMenu(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseMove(e: IEditorMouseEvent): void { + if (this.onMouseMove) { + this.onMouseMove(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseLeave(e: IPartialEditorMouseEvent): void { + if (this.onMouseLeave) { + this.onMouseLeave(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseDown(e: IEditorMouseEvent): void { + if (this.onMouseDown) { + this.onMouseDown(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseUp(e: IEditorMouseEvent): void { + if (this.onMouseUp) { + this.onMouseUp(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseDrag(e: IEditorMouseEvent): void { + if (this.onMouseDrag) { + this.onMouseDrag(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseDrop(e: IPartialEditorMouseEvent): void { + if (this.onMouseDrop) { + this.onMouseDrop(this._convertViewToModelMouseEvent(e)); + } + } + + public emitMouseWheel(e: IMouseWheelEvent): void { + if (this.onMouseWheel) { + this.onMouseWheel(e); + } + } + + private _convertViewToModelMouseEvent(e: IEditorMouseEvent): IEditorMouseEvent; + private _convertViewToModelMouseEvent(e: IPartialEditorMouseEvent): IPartialEditorMouseEvent; + private _convertViewToModelMouseEvent(e: IEditorMouseEvent | IPartialEditorMouseEvent): IEditorMouseEvent | IPartialEditorMouseEvent { + if (e.target) { + return { + event: e.event, + target: this._convertViewToModelMouseTarget(e.target) + }; + } + return e; + } + + private _convertViewToModelMouseTarget(target: IMouseTarget): IMouseTarget { + return ViewUserInputEvents.convertViewToModelMouseTarget(target, this._coordinatesConverter); + } + + public static convertViewToModelMouseTarget(target: IMouseTarget, coordinatesConverter: ICoordinatesConverter): IMouseTarget { + return new ExternalMouseTarget( + target.element, + target.type, + target.mouseColumn, + target.position ? coordinatesConverter.convertViewPositionToModelPosition(target.position) : null, + target.range ? coordinatesConverter.convertViewRangeToModelRange(target.range) : null, + target.detail + ); + } +} + +class ExternalMouseTarget implements IMouseTarget { + + public readonly element: Element | null; + public readonly type: MouseTargetType; + public readonly mouseColumn: number; + public readonly position: Position | null; + public readonly range: Range | null; + public readonly detail: any; + + constructor(element: Element | null, type: MouseTargetType, mouseColumn: number, position: Position | null, range: Range | null, detail: any) { + this.element = element; + this.type = type; + this.mouseColumn = mouseColumn; + this.position = position; + this.range = range; + this.detail = detail; + } + + public toString(): string { + return MouseTarget.toString(this); + } +} diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index e98cea19e9c61..a3a1643684654 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -7,7 +7,6 @@ import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ContentWidgetPositionPreference, IContentWidget } from 'vs/editor/browser/editorBrowser'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; -import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Constants } from 'vs/base/common/uint'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; @@ -112,9 +111,9 @@ export class ViewContentWidgets extends ViewPart { this.setShouldRender(); } - public setWidgetPosition(widget: IContentWidget, position: IPosition | null, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void { + public setWidgetPosition(widget: IContentWidget, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void { const myWidget = this._widgets[widget.getId()]; - myWidget.setPosition(position, range, preference); + myWidget.setPosition(range, preference); this.setShouldRender(); } @@ -187,8 +186,6 @@ class Widget { private _contentLeft: number; private _lineHeight: number; - private _position: IPosition | null; - private _viewPosition: Position | null; private _range: IRange | null; private _viewRange: Range | null; private _preference: ContentWidgetPositionPreference[] | null; @@ -217,9 +214,7 @@ class Widget { this._contentLeft = layoutInfo.contentLeft; this._lineHeight = options.get(EditorOption.lineHeight); - this._position = null; this._range = null; - this._viewPosition = null; this._viewRange = null; this._preference = []; this._cachedDomNodeClientWidth = -1; @@ -246,26 +241,19 @@ class Widget { } public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): void { - this._setPosition(this._position, this._range); + this._setPosition(this._range); } - private _setPosition(position: IPosition | null, range: IRange | null): void { - this._position = position; + private _setPosition(range: IRange | null): void { this._range = range; - this._viewPosition = null; this._viewRange = null; - if (this._position) { - // Do not trust that widgets give a valid position - const validModelPosition = this._context.model.validateModelPosition(this._position); - if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) { - this._viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition); - } - } if (this._range) { // Do not trust that widgets give a valid position const validModelRange = this._context.model.validateModelRange(this._range); - this._viewRange = this._context.model.coordinatesConverter.convertModelRangeToViewRange(validModelRange); + if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelRange.getStartPosition()) || this._context.model.coordinatesConverter.modelPositionIsVisible(validModelRange.getEndPosition())) { + this._viewRange = this._context.model.coordinatesConverter.convertModelRangeToViewRange(validModelRange); + } } } @@ -277,8 +265,8 @@ class Widget { ); } - public setPosition(position: IPosition | null, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void { - this._setPosition(position, range); + public setPosition(range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void { + this._setPosition(range); this._preference = preference; this._cachedDomNodeClientWidth = -1; this._cachedDomNodeClientHeight = -1; @@ -327,60 +315,66 @@ class Widget { }; } - private _layoutBoxInPage(topLeft: Coordinate, bottomLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult | null { - const aboveLeft0 = topLeft.left - ctx.scrollLeft; - const belowLeft0 = bottomLeft.left - ctx.scrollLeft; + private _layoutHorizontalSegmentInPage(windowSize: dom.Dimension, domNodePosition: dom.IDomNodePagePosition, left: number, width: number): [number, number] { + // Initially, the limits are defined as the dom node limits + const MIN_LIMIT = Math.max(0, domNodePosition.left - width); + const MAX_LIMIT = Math.min(domNodePosition.left + domNodePosition.width + width, windowSize.width); + + let absoluteLeft = domNodePosition.left + left - dom.StandardWindow.scrollX; + + if (absoluteLeft + width > MAX_LIMIT) { + const delta = absoluteLeft - (MAX_LIMIT - width); + absoluteLeft -= delta; + left -= delta; + } - let aboveTop = topLeft.top - height; - let belowTop = bottomLeft.top + this._lineHeight; - let aboveLeft = aboveLeft0 + this._contentLeft; - let belowLeft = belowLeft0 + this._contentLeft; + if (absoluteLeft < MIN_LIMIT) { + const delta = absoluteLeft - MIN_LIMIT; + absoluteLeft -= delta; + left -= delta; + } + + return [left, absoluteLeft]; + } + + private _layoutBoxInPage(topLeft: Coordinate, bottomLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult | null { + const aboveTop = topLeft.top - height; + const belowTop = bottomLeft.top + this._lineHeight; const domNodePosition = dom.getDomNodePagePosition(this._viewDomNode.domNode); const absoluteAboveTop = domNodePosition.top + aboveTop - dom.StandardWindow.scrollY; const absoluteBelowTop = domNodePosition.top + belowTop - dom.StandardWindow.scrollY; - let absoluteAboveLeft = domNodePosition.left + aboveLeft - dom.StandardWindow.scrollX; - let absoluteBelowLeft = domNodePosition.left + belowLeft - dom.StandardWindow.scrollX; - const INNER_WIDTH = window.innerWidth || document.documentElement!.clientWidth || document.body.clientWidth; - const INNER_HEIGHT = window.innerHeight || document.documentElement!.clientHeight || document.body.clientHeight; + const windowSize = dom.getClientArea(document.body); + const [aboveLeft, absoluteAboveLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, topLeft.left - ctx.scrollLeft + this._contentLeft, width); + const [belowLeft, absoluteBelowLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, bottomLeft.left - ctx.scrollLeft + this._contentLeft, width); - // Leave some clearance to the bottom + // Leave some clearance to the top/bottom const TOP_PADDING = 22; const BOTTOM_PADDING = 22; - const fitsAbove = (absoluteAboveTop >= TOP_PADDING), - fitsBelow = (absoluteBelowTop + height <= INNER_HEIGHT - BOTTOM_PADDING); - - if (absoluteAboveLeft + width + 20 > INNER_WIDTH) { - const delta = absoluteAboveLeft - (INNER_WIDTH - width - 20); - absoluteAboveLeft -= delta; - aboveLeft -= delta; - } - if (absoluteBelowLeft + width + 20 > INNER_WIDTH) { - const delta = absoluteBelowLeft - (INNER_WIDTH - width - 20); - absoluteBelowLeft -= delta; - belowLeft -= delta; - } - if (absoluteAboveLeft < 0) { - const delta = absoluteAboveLeft; - absoluteAboveLeft -= delta; - aboveLeft -= delta; - } - if (absoluteBelowLeft < 0) { - const delta = absoluteBelowLeft; - absoluteBelowLeft -= delta; - belowLeft -= delta; - } + const fitsAbove = (absoluteAboveTop >= TOP_PADDING); + const fitsBelow = (absoluteBelowTop + height <= windowSize.height - BOTTOM_PADDING); if (this._fixedOverflowWidgets) { - aboveTop = absoluteAboveTop; - belowTop = absoluteBelowTop; - aboveLeft = absoluteAboveLeft; - belowLeft = absoluteBelowLeft; + return { + fitsAbove, + aboveTop: Math.max(absoluteAboveTop, TOP_PADDING), + aboveLeft: absoluteAboveLeft, + fitsBelow, + belowTop: absoluteBelowTop, + belowLeft: absoluteBelowLeft + }; } - return { fitsAbove, aboveTop: Math.max(aboveTop, TOP_PADDING), aboveLeft, fitsBelow, belowTop, belowLeft }; + return { + fitsAbove, + aboveTop: aboveTop, + aboveLeft, + fitsBelow, + belowTop, + belowLeft + }; } private _prepareRenderWidgetAtExactPositionOverflowing(topLeft: Coordinate): Coordinate { @@ -391,45 +385,45 @@ class Widget { * Compute `this._topLeft` */ private _getTopAndBottomLeft(ctx: RenderingContext): [Coordinate, Coordinate] | [null, null] { - if (!this._viewPosition) { + if (!this._viewRange) { return [null, null]; } - const visibleRangeForPosition = ctx.visibleRangeForPosition(this._viewPosition); - if (!visibleRangeForPosition) { + const visibleRangesForRange = ctx.linesVisibleRangesForRange(this._viewRange, false); + if (!visibleRangesForRange || visibleRangesForRange.length === 0) { return [null, null]; } - const topForPosition = ctx.getVerticalOffsetForLineNumber(this._viewPosition.lineNumber) - ctx.scrollTop; - const topLeft = new Coordinate(topForPosition, visibleRangeForPosition.left); - - let largestLineNumber = this._viewPosition.lineNumber; - let smallestLeft = visibleRangeForPosition.left; - - if (this._viewRange) { - const visibleRangesForRange = ctx.linesVisibleRangesForRange(this._viewRange, false); - if (visibleRangesForRange && visibleRangesForRange.length > 0) { - for (let i = visibleRangesForRange.length - 1; i >= 0; i--) { - const visibleRangesForLine = visibleRangesForRange[i]; - if (visibleRangesForLine.lineNumber >= largestLineNumber) { - if (visibleRangesForLine.lineNumber > largestLineNumber) { - largestLineNumber = visibleRangesForLine.lineNumber; - smallestLeft = Constants.MAX_SAFE_SMALL_INTEGER; - } - for (let j = 0, lenJ = visibleRangesForLine.ranges.length; j < lenJ; j++) { - const visibleRange = visibleRangesForLine.ranges[j]; + let firstLine = visibleRangesForRange[0]; + let lastLine = visibleRangesForRange[0]; + for (const visibleRangesForLine of visibleRangesForRange) { + if (visibleRangesForLine.lineNumber < firstLine.lineNumber) { + firstLine = visibleRangesForLine; + } + if (visibleRangesForLine.lineNumber > lastLine.lineNumber) { + lastLine = visibleRangesForLine; + } + } - if (visibleRange.left < smallestLeft) { - smallestLeft = visibleRange.left; - } - } - } - } + let firstLineMinLeft = Constants.MAX_SAFE_SMALL_INTEGER;//firstLine.Constants.MAX_SAFE_SMALL_INTEGER; + for (const visibleRange of firstLine.ranges) { + if (visibleRange.left < firstLineMinLeft) { + firstLineMinLeft = visibleRange.left; } } - const topForBottomLine = ctx.getVerticalOffsetForLineNumber(largestLineNumber) - ctx.scrollTop; - const bottomLeft = new Coordinate(topForBottomLine, smallestLeft); + let lastLineMinLeft = Constants.MAX_SAFE_SMALL_INTEGER;//lastLine.Constants.MAX_SAFE_SMALL_INTEGER; + for (const visibleRange of lastLine.ranges) { + if (visibleRange.left < lastLineMinLeft) { + lastLineMinLeft = visibleRange.left; + } + } + + const topForPosition = ctx.getVerticalOffsetForLineNumber(firstLine.lineNumber) - ctx.scrollTop; + const topLeft = new Coordinate(topForPosition, firstLineMinLeft); + + const topForBottomLine = ctx.getVerticalOffsetForLineNumber(lastLine.lineNumber) - ctx.scrollTop; + const bottomLeft = new Coordinate(topForBottomLine, lastLineMinLeft); return [topLeft, bottomLeft]; } @@ -491,11 +485,11 @@ class Widget { * On this first pass, we ensure that the content widget (if it is in the viewport) has the max width set correctly. */ public onBeforeRender(viewportData: ViewportData): void { - if (!this._viewPosition || !this._preference) { + if (!this._viewRange || !this._preference) { return; } - if (this._viewPosition.lineNumber < viewportData.startLineNumber || this._viewPosition.lineNumber > viewportData.endLineNumber) { + if (this._viewRange.endLineNumber < viewportData.startLineNumber || this._viewRange.startLineNumber > viewportData.endLineNumber) { // Outside of viewport return; } diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts index cb3e355d8c628..098bc6c76fa75 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -23,6 +23,8 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { protected _contentLeft: number; protected _contentWidth: number; protected _selectionIsEmpty: boolean; + protected _renderLineHightlightOnlyWhenFocus: boolean; + protected _focused: boolean; private _cursorLineNumbers: number[]; private _selections: Selection[]; private _renderData: string[] | null; @@ -35,9 +37,11 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { const layoutInfo = options.get(EditorOption.layoutInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._renderLineHighlight = options.get(EditorOption.renderLineHighlight); + this._renderLineHightlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; this._selectionIsEmpty = true; + this._focused = false; this._cursorLineNumbers = []; this._selections = []; this._renderData = null; @@ -57,7 +61,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { const renderSelections = isRenderedUsingBorder ? this._selections.slice(0, 1) : this._selections; const cursorsLineNumbers = renderSelections.map(s => s.positionLineNumber); - cursorsLineNumbers.sort(); + cursorsLineNumbers.sort((a, b) => a - b); if (!arrays.equals(this._cursorLineNumbers, cursorsLineNumbers)) { this._cursorLineNumbers = cursorsLineNumbers; hasChanged = true; @@ -81,6 +85,7 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { const layoutInfo = options.get(EditorOption.layoutInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._renderLineHighlight = options.get(EditorOption.renderLineHighlight); + this._renderLineHightlightOnlyWhenFocus = options.get(EditorOption.renderLineHighlightOnlyWhenFocus); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; return true; @@ -104,6 +109,14 @@ export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { return true; } + public onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean { + if (!this._renderLineHightlightOnlyWhenFocus) { + return false; + } + + this._focused = e.isFocused; + return true; + } // --- end event handlers public prepareRender(ctx: RenderingContext): void { @@ -157,11 +170,13 @@ export class CurrentLineHighlightOverlay extends AbstractLineHighlightOverlay { return ( (this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all') && this._selectionIsEmpty + && (!this._renderLineHightlightOnlyWhenFocus || this._focused) ); } protected _shouldRenderOther(): boolean { return ( (this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all') + && (!this._renderLineHightlightOnlyWhenFocus || this._focused) ); } } @@ -174,12 +189,14 @@ export class CurrentLineMarginHighlightOverlay extends AbstractLineHighlightOver protected _shouldRenderThis(): boolean { return ( (this._renderLineHighlight === 'gutter' || this._renderLineHighlight === 'all') + && (!this._renderLineHightlightOnlyWhenFocus || this._focused) ); } protected _shouldRenderOther(): boolean { return ( (this._renderLineHighlight === 'line' || this._renderLineHighlight === 'all') && this._selectionIsEmpty + && (!this._renderLineHightlightOnlyWhenFocus || this._focused) ); } } diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index d816f1265e829..21193bac130b6 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -9,7 +9,7 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IOverviewRulerLayoutInfo, SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollableElementChangeOptions, ScrollableElementCreationOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; -import { INewScrollPosition } from 'vs/editor/common/editorCommon'; +import { INewScrollPosition, ScrollType } from 'vs/editor/common/editorCommon'; 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'; @@ -34,6 +34,7 @@ export class EditorScrollbar extends ViewPart { const scrollbar = options.get(EditorOption.scrollbar); const mouseWheelScrollSensitivity = options.get(EditorOption.mouseWheelScrollSensitivity); const fastScrollSensitivity = options.get(EditorOption.fastScrollSensitivity); + const scrollPredominantAxis = options.get(EditorOption.scrollPredominantAxis); const scrollbarOptions: ScrollableElementCreationOptions = { listenOnDomNode: viewDomNode.domNode, @@ -50,12 +51,14 @@ export class EditorScrollbar extends ViewPart { horizontalScrollbarSize: scrollbar.horizontalScrollbarSize, horizontalSliderSize: scrollbar.horizontalSliderSize, handleMouseWheel: scrollbar.handleMouseWheel, + alwaysConsumeMouseWheel: scrollbar.alwaysConsumeMouseWheel, arrowSize: scrollbar.arrowSize, mouseWheelScrollSensitivity: mouseWheelScrollSensitivity, fastScrollSensitivity: fastScrollSensitivity, + scrollPredominantAxis: scrollPredominantAxis, }; - this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.scrollable)); + this.scrollbar = this._register(new SmoothScrollableElement(linesContent.domNode, scrollbarOptions, this._context.viewLayout.getScrollable())); PartFingerprints.write(this.scrollbar.getDomNode(), PartFingerprint.ScrollableElement); this.scrollbarDomNode = createFastDomNode(this.scrollbar.getDomNode()); @@ -85,7 +88,7 @@ export class EditorScrollbar extends ViewPart { } } - this._context.viewLayout.setScrollPositionNow(newScrollPosition); + this._context.model.setScrollPosition(newScrollPosition, ScrollType.Immediate); }; // I've seen this happen both on the view dom node & on the lines content dom node. @@ -108,11 +111,11 @@ export class EditorScrollbar extends ViewPart { const minimap = options.get(EditorOption.minimap); const side = minimap.side; if (side === 'right') { - this.scrollbarDomNode.setWidth(layoutInfo.contentWidth + layoutInfo.minimapWidth); + this.scrollbarDomNode.setWidth(layoutInfo.contentWidth + layoutInfo.minimap.minimapWidth); } else { this.scrollbarDomNode.setWidth(layoutInfo.contentWidth); } - this.scrollbarDomNode.setHeight(layoutInfo.contentHeight); + this.scrollbarDomNode.setHeight(layoutInfo.height); } public getOverviewRulerLayoutInfo(): IOverviewRulerLayoutInfo { @@ -139,10 +142,12 @@ export class EditorScrollbar extends ViewPart { const scrollbar = options.get(EditorOption.scrollbar); const mouseWheelScrollSensitivity = options.get(EditorOption.mouseWheelScrollSensitivity); const fastScrollSensitivity = options.get(EditorOption.fastScrollSensitivity); + const scrollPredominantAxis = options.get(EditorOption.scrollPredominantAxis); const newOpts: ScrollableElementChangeOptions = { handleMouseWheel: scrollbar.handleMouseWheel, mouseWheelScrollSensitivity: mouseWheelScrollSensitivity, - fastScrollSensitivity: fastScrollSensitivity + fastScrollSensitivity: fastScrollSensitivity, + scrollPredominantAxis: scrollPredominantAxis }; this.scrollbar.updateOptions(newOpts); } diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts index 494b7d7ca233e..0cdd9c61cc2ed 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -177,7 +177,7 @@ export class GlyphMarginOverlay extends DedupOverlay { output[lineIndex] = ''; } else { output[lineIndex] = ( - '
    `; - left += indentWidth; - if (left > scrollWidth || (this._maxIndentLeft > 0 && left > this._maxIndentLeft)) { - break; + if (indent >= 1) { + const leftMostVisiblePosition = ctx.visibleRangeForPosition(new Position(lineNumber, 1)); + let left = leftMostVisiblePosition ? leftMostVisiblePosition.left : 0; + for (let i = 1; i <= indent; i++) { + const className = (containsActiveIndentGuide && i === activeIndentLevel ? 'cigra' : 'cigr'); + result += `
    `; + left += indentWidth; + if (left > scrollWidth || (this._maxIndentLeft > 0 && left > this._maxIndentLeft)) { + break; + } } } diff --git a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg deleted file mode 100644 index 1d1cda0e93e29..0000000000000 --- a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-2x.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac-2x.svg b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac-2x.svg deleted file mode 100644 index 7f64457f78be9..0000000000000 --- a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac-2x.svg +++ /dev/null @@ -1,2 +0,0 @@ - - flipped-cursor-mac diff --git a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac.svg b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac.svg deleted file mode 100644 index da79a9547db10..0000000000000 --- a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor-mac.svg +++ /dev/null @@ -1 +0,0 @@ -flipped-cursor-mac diff --git a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor.svg b/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor.svg deleted file mode 100644 index 0add3031fb5eb..0000000000000 --- a/src/vs/editor/browser/viewParts/lineNumbers/flipped-cursor.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css index 3cfe5e758e2d7..02def4da31f3f 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ .monaco-editor .margin-view-overlays .line-numbers { + font-variant-numeric: tabular-nums; position: absolute; text-align: right; display: inline-block; @@ -19,20 +20,6 @@ width: 100%; } -.monaco-editor .margin-view-overlays .line-numbers { - cursor: -webkit-image-set( - url('flipped-cursor.svg') 1x, - url('flipped-cursor-2x.svg') 2x - ) 30 0, default; -} - -.monaco-editor.mac .margin-view-overlays .line-numbers { - cursor: -webkit-image-set( - url('flipped-cursor-mac.svg') 1x, - url('flipped-cursor-mac-2x.svg') 2x - ) 24 3, default; -} - .monaco-editor .margin-view-overlays .line-numbers.lh-odd { margin-top: 1px; } diff --git a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts index f84a79df8d4b6..7718ae4003ac3 100644 --- a/src/vs/editor/browser/viewParts/lines/rangeUtil.ts +++ b/src/vs/editor/browser/viewParts/lines/rangeUtil.ts @@ -121,6 +121,13 @@ export class RangeUtil { startChildIndex = Math.min(max, Math.max(min, startChildIndex)); endChildIndex = Math.min(max, Math.max(min, endChildIndex)); + if (startChildIndex === endChildIndex && startOffset === endOffset && startOffset === 0) { + // We must find the position at the beginning of a + // To cover cases of empty s, aboid using a range and use the 's bounding box + const clientRects = domNode.children[startChildIndex].getClientRects(); + return this._createHorizontalRangesFromClientRects(clientRects, clientRectDeltaLeft); + } + // If crossing over to a span only to select offset 0, then use the previous span's maximum offset // Chrome is buggy and doesn't handle 0 offsets well sometimes. if (startChildIndex !== endChildIndex) { diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 655b93462d31c..d75928f24ae74 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -15,7 +15,7 @@ import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { CharacterMapping, ForeignElementType, RenderLineInput, renderViewLine, LineRange } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { InlineDecorationType } from 'vs/editor/common/viewModel/viewModel'; -import { HIGH_CONTRAST, ThemeType } from 'vs/platform/theme/common/themeService'; +import { ColorScheme } from 'vs/platform/theme/common/theme'; import { EditorOption, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; const canUseFastRenderedViewLine = (function () { @@ -42,7 +42,9 @@ const canUseFastRenderedViewLine = (function () { return true; })(); -const alwaysRenderInlineSelection = (browser.isEdgeOrIE); +let monospaceAssumptionsAreValid = true; + +const alwaysRenderInlineSelection = (browser.isEdge); export class DomReadingContext { @@ -69,23 +71,27 @@ export class DomReadingContext { } export class ViewLineOptions { - public readonly themeType: ThemeType; - public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'all'; + public readonly themeType: ColorScheme; + public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'trailing' | 'all'; public readonly renderControlCharacters: boolean; public readonly spaceWidth: number; + public readonly middotWidth: number; + public readonly wsmiddotWidth: number; public readonly useMonospaceOptimizations: boolean; public readonly canUseHalfwidthRightwardsArrow: boolean; public readonly lineHeight: number; public readonly stopRenderingLineAfter: number; public readonly fontLigatures: string; - constructor(config: IConfiguration, themeType: ThemeType) { + constructor(config: IConfiguration, themeType: ColorScheme) { this.themeType = themeType; const options = config.options; const fontInfo = options.get(EditorOption.fontInfo); this.renderWhitespace = options.get(EditorOption.renderWhitespace); this.renderControlCharacters = options.get(EditorOption.renderControlCharacters); this.spaceWidth = fontInfo.spaceWidth; + this.middotWidth = fontInfo.middotWidth; + this.wsmiddotWidth = fontInfo.wsmiddotWidth; this.useMonospaceOptimizations = ( fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations) @@ -102,6 +108,8 @@ export class ViewLineOptions { && this.renderWhitespace === other.renderWhitespace && this.renderControlCharacters === other.renderControlCharacters && this.spaceWidth === other.spaceWidth + && this.middotWidth === other.middotWidth + && this.wsmiddotWidth === other.wsmiddotWidth && this.useMonospaceOptimizations === other.useMonospaceOptimizations && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow && this.lineHeight === other.lineHeight @@ -155,7 +163,7 @@ export class ViewLine implements IVisibleLine { this._options = newOptions; } public onSelectionChanged(): boolean { - if (alwaysRenderInlineSelection || this._options.themeType === HIGH_CONTRAST || this._options.renderWhitespace === 'selection') { + if (alwaysRenderInlineSelection || this._options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === 'selection') { this._isMaybeInvalid = true; return true; } @@ -176,7 +184,7 @@ export class ViewLine implements IVisibleLine { // Only send selection information when needed for rendering whitespace let selectionsOnLine: LineRange[] | null = null; - if (alwaysRenderInlineSelection || options.themeType === HIGH_CONTRAST || this._options.renderWhitespace === 'selection') { + if (alwaysRenderInlineSelection || options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === 'selection') { const selections = viewportData.selections; for (const selection of selections) { @@ -189,7 +197,7 @@ export class ViewLine implements IVisibleLine { const endColumn = (selection.endLineNumber === lineNumber ? selection.endColumn : lineData.maxColumn); if (startColumn < endColumn) { - if (this._options.renderWhitespace !== 'selection') { + if (options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace !== 'selection') { actualInlineDecorations.push(new LineDecoration(startColumn, endColumn, 'inline-selected-text', InlineDecorationType.Regular)); } else { if (!selectionsOnLine) { @@ -213,7 +221,10 @@ export class ViewLine implements IVisibleLine { lineData.tokens, actualInlineDecorations, lineData.tabSize, + lineData.startVisibleColumn, options.spaceWidth, + options.middotWidth, + options.wsmiddotWidth, options.stopRenderingLineAfter, options.renderWhitespace, options.renderControlCharacters, @@ -239,7 +250,7 @@ export class ViewLine implements IVisibleLine { sb.appendASCIIString(''); let renderedViewLine: IRenderedViewLine | null = null; - if (canUseFastRenderedViewLine && lineData.isBasicASCII && options.useMonospaceOptimizations && output.containsForeignElements === ForeignElementType.None) { + if (monospaceAssumptionsAreValid && canUseFastRenderedViewLine && lineData.isBasicASCII && options.useMonospaceOptimizations && output.containsForeignElements === ForeignElementType.None) { if (lineData.content.length < 300 && renderLineInput.lineTokens.getCount() < 100) { // Browser rounding errors have been observed in Chrome and IE, so using the fast // view line only for short lines. Please test before removing the length check... @@ -295,6 +306,29 @@ export class ViewLine implements IVisibleLine { return this._renderedViewLine.getWidthIsFast(); } + public needsMonospaceFontCheck(): boolean { + if (!this._renderedViewLine) { + return false; + } + return (this._renderedViewLine instanceof FastRenderedViewLine); + } + + public monospaceAssumptionsAreValid(): boolean { + if (!this._renderedViewLine) { + return monospaceAssumptionsAreValid; + } + if (this._renderedViewLine instanceof FastRenderedViewLine) { + return this._renderedViewLine.monospaceAssumptionsAreValid(); + } + return monospaceAssumptionsAreValid; + } + + public onMonospaceAssumptionsInvalidated(): void { + if (this._renderedViewLine && this._renderedViewLine instanceof FastRenderedViewLine) { + this._renderedViewLine = this._renderedViewLine.toSlowRenderedLine(); + } + } + public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): VisibleRanges | null { if (!this._renderedViewLine) { return null; @@ -373,6 +407,24 @@ class FastRenderedViewLine implements IRenderedViewLine { return true; } + public monospaceAssumptionsAreValid(): boolean { + if (!this.domNode) { + return monospaceAssumptionsAreValid; + } + const expectedWidth = this.getWidth(); + const actualWidth = (this.domNode.domNode.firstChild).offsetWidth; + if (Math.abs(expectedWidth - actualWidth) >= 2) { + // more than 2px off + console.warn(`monospace assumptions have been violated, therefore disabling monospace optimizations!`); + monospaceAssumptionsAreValid = false; + } + return monospaceAssumptionsAreValid; + } + + public toSlowRenderedLine(): RenderedViewLine { + return createRenderedLine(this.domNode, this.input, this._characterMapping, false, ForeignElementType.None); + } + public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { const startPosition = this._getCharPosition(startColumn); const endPosition = this._getCharPosition(endColumn); @@ -407,7 +459,7 @@ class FastRenderedViewLine implements IRenderedViewLine { */ class RenderedViewLine implements IRenderedViewLine { - public domNode: FastDomNode; + public domNode: FastDomNode | null; public readonly input: RenderLineInput; protected readonly _characterMapping: CharacterMapping; @@ -420,7 +472,7 @@ class RenderedViewLine implements IRenderedViewLine { */ private readonly _pixelOffsetCache: Int32Array | null; - constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType) { + constructor(domNode: FastDomNode | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType) { this.domNode = domNode; this.input = renderLineInput; this._characterMapping = characterMapping; @@ -439,16 +491,19 @@ class RenderedViewLine implements IRenderedViewLine { // --- Reading from the DOM methods - protected _getReadingTarget(): HTMLElement { - return this.domNode.domNode.firstChild; + protected _getReadingTarget(myDomNode: FastDomNode): HTMLElement { + return myDomNode.domNode.firstChild; } /** * Width of the line in pixels */ public getWidth(): number { + if (!this.domNode) { + return 0; + } if (this._cachedWidth === -1) { - this._cachedWidth = this._getReadingTarget().offsetWidth; + this._cachedWidth = this._getReadingTarget(this.domNode).offsetWidth; } return this._cachedWidth; } @@ -464,14 +519,17 @@ class RenderedViewLine implements IRenderedViewLine { * Visible ranges for a model range */ public getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + if (!this.domNode) { + return null; + } if (this._pixelOffsetCache !== null) { // the text is LTR - const startOffset = this._readPixelOffset(startColumn, context); + const startOffset = this._readPixelOffset(this.domNode, startColumn, context); if (startOffset === -1) { return null; } - const endOffset = this._readPixelOffset(endColumn, context); + const endOffset = this._readPixelOffset(this.domNode, endColumn, context); if (endOffset === -1) { return null; } @@ -479,23 +537,23 @@ class RenderedViewLine implements IRenderedViewLine { return [new HorizontalRange(startOffset, endOffset - startOffset)]; } - return this._readVisibleRangesForRange(startColumn, endColumn, context); + return this._readVisibleRangesForRange(this.domNode, startColumn, endColumn, context); } - protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + protected _readVisibleRangesForRange(domNode: FastDomNode, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { if (startColumn === endColumn) { - const pixelOffset = this._readPixelOffset(startColumn, context); + const pixelOffset = this._readPixelOffset(domNode, startColumn, context); if (pixelOffset === -1) { return null; } else { return [new HorizontalRange(pixelOffset, 0)]; } } else { - return this._readRawVisibleRangesForRange(startColumn, endColumn, context); + return this._readRawVisibleRangesForRange(domNode, startColumn, endColumn, context); } } - protected _readPixelOffset(column: number, context: DomReadingContext): number { + protected _readPixelOffset(domNode: FastDomNode, column: number, context: DomReadingContext): number { if (this._characterMapping.length === 0) { // This line has no content if (this._containsForeignElements === ForeignElementType.None) { @@ -507,9 +565,16 @@ class RenderedViewLine implements IRenderedViewLine { return 0; } if (this._containsForeignElements === ForeignElementType.Before) { - // We have foreign element before the (empty) line + // We have foreign elements before the (empty) line return this.getWidth(); } + // We have foreign elements before & after the (empty) line + const readingTarget = this._getReadingTarget(domNode); + if (readingTarget.firstChild) { + return (readingTarget.firstChild).offsetWidth; + } else { + return 0; + } } if (this._pixelOffsetCache !== null) { @@ -520,18 +585,18 @@ class RenderedViewLine implements IRenderedViewLine { return cachedPixelOffset; } - const result = this._actualReadPixelOffset(column, context); + const result = this._actualReadPixelOffset(domNode, column, context); this._pixelOffsetCache[column] = result; return result; } - return this._actualReadPixelOffset(column, context); + return this._actualReadPixelOffset(domNode, column, context); } - private _actualReadPixelOffset(column: number, context: DomReadingContext): number { + private _actualReadPixelOffset(domNode: FastDomNode, column: number, context: DomReadingContext): number { if (this._characterMapping.length === 0) { // This line has no content - const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode); + const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), 0, 0, 0, 0, context.clientRectDeltaLeft, context.endNode); if (!r || r.length === 0) { return -1; } @@ -547,14 +612,22 @@ class RenderedViewLine implements IRenderedViewLine { const partIndex = CharacterMapping.getPartIndex(partData); const charOffsetInPart = CharacterMapping.getCharIndex(partData); - const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode); + const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), partIndex, charOffsetInPart, partIndex, charOffsetInPart, context.clientRectDeltaLeft, context.endNode); if (!r || r.length === 0) { return -1; } - return r[0].left; + const result = r[0].left; + if (this.input.isBasicASCII) { + const charOffset = this._characterMapping.getAbsoluteOffsets(); + const expectedResult = Math.round(this.input.spaceWidth * charOffset[column - 1]); + if (Math.abs(expectedResult - result) <= 1) { + return expectedResult; + } + } + return result; } - private _readRawVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + private _readRawVisibleRangesForRange(domNode: FastDomNode, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { if (startColumn === 1 && endColumn === this._characterMapping.length) { // This branch helps IE with bidi text & gives a performance boost to other browsers when reading visible ranges for an entire line @@ -570,7 +643,7 @@ class RenderedViewLine implements IRenderedViewLine { const endPartIndex = CharacterMapping.getPartIndex(endPartData); const endCharOffsetInPart = CharacterMapping.getCharIndex(endPartData); - return RangeUtil.readHorizontalRanges(this._getReadingTarget(), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, context.clientRectDeltaLeft, context.endNode); + return RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, context.clientRectDeltaLeft, context.endNode); } /** @@ -591,8 +664,8 @@ class RenderedViewLine implements IRenderedViewLine { } class WebKitRenderedViewLine extends RenderedViewLine { - protected _readVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { - const output = super._readVisibleRangesForRange(startColumn, endColumn, context); + protected _readVisibleRangesForRange(domNode: FastDomNode, startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[] | null { + const output = super._readVisibleRangesForRange(domNode, startColumn, endColumn, context); if (!output || output.length === 0 || startColumn === endColumn || (startColumn === 1 && endColumn === this._characterMapping.length)) { return output; @@ -603,7 +676,7 @@ class WebKitRenderedViewLine extends RenderedViewLine { if (!this.input.containsRTL) { // This is an attempt to patch things up // Find position of last column - const endPixelOffset = this._readPixelOffset(endColumn, context); + const endPixelOffset = this._readPixelOffset(domNode, endColumn, context); if (endPixelOffset !== -1) { const lastRange = output[output.length - 1]; if (lastRange.left < endPixelOffset) { @@ -624,10 +697,10 @@ const createRenderedLine: (domNode: FastDomNode | null, renderLineI return createNormalRenderedLine; })(); -function createWebKitRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { +function createWebKitRenderedLine(domNode: FastDomNode | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { return new WebKitRenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements); } -function createNormalRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { +function createNormalRenderedLine(domNode: FastDomNode | null, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean, containsForeignElements: ForeignElementType): RenderedViewLine { return new RenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements); } diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.css b/src/vs/editor/browser/viewParts/lines/viewLines.css index b6b58faacb6db..2db08e1542808 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.css +++ b/src/vs/editor/browser/viewParts/lines/viewLines.css @@ -23,20 +23,18 @@ } .monaco-editor .view-lines { - cursor: text; white-space: nowrap; } -.monaco-editor.vs-dark.mac .view-lines, -.monaco-editor.hc-black.mac .view-lines { - 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; -} - .monaco-editor .view-line { position: absolute; width: 100%; } +.monaco-editor .mtkz { + display: inline-block; +} + /* TODO@tokenization bootstrap fix */ /*.monaco-editor .view-line > span > span { float: none; diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 852d612598508..6e92c558e1167 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./viewLines'; +import * as platform from 'vs/base/common/platform'; import { FastDomNode } from 'vs/base/browser/fastDomNode'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Configuration } from 'vs/editor/browser/config/configuration'; @@ -12,6 +13,7 @@ import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/v import { DomReadingContext, ViewLine, ViewLineOptions } from 'vs/editor/browser/viewParts/lines/viewLine'; 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 { ScrollType } from 'vs/editor/common/editorCommon'; import { IViewLines, LineVisibleRanges, VisibleRanges, HorizontalPosition } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; @@ -20,6 +22,7 @@ import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData' import { Viewport } from 'vs/editor/common/viewModel/viewModel'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Constants } from 'vs/base/common/uint'; +import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; class LastRenderedData { @@ -38,25 +41,49 @@ class LastRenderedData { } } -class HorizontalRevealRequest { - - public readonly lineNumber: number; - public readonly startColumn: number; - public readonly endColumn: number; - public readonly startScrollTop: number; - public readonly stopScrollTop: number; - public readonly scrollType: ScrollType; +class HorizontalRevealRangeRequest { + public readonly type = 'range'; + public readonly minLineNumber: number; + public readonly maxLineNumber: number; + + constructor( + public readonly lineNumber: number, + public readonly startColumn: number, + public readonly endColumn: number, + public readonly startScrollTop: number, + public readonly stopScrollTop: number, + public readonly scrollType: ScrollType + ) { + this.minLineNumber = lineNumber; + this.maxLineNumber = lineNumber; + } +} - constructor(lineNumber: number, startColumn: number, endColumn: number, startScrollTop: number, stopScrollTop: number, scrollType: ScrollType) { - this.lineNumber = lineNumber; - this.startColumn = startColumn; - this.endColumn = endColumn; - this.startScrollTop = startScrollTop; - this.stopScrollTop = stopScrollTop; - this.scrollType = scrollType; +class HorizontalRevealSelectionsRequest { + public readonly type = 'selections'; + public readonly minLineNumber: number; + public readonly maxLineNumber: number; + + constructor( + public readonly selections: Selection[], + public readonly startScrollTop: number, + public readonly stopScrollTop: number, + public readonly scrollType: ScrollType + ) { + let minLineNumber = selections[0].startLineNumber; + let maxLineNumber = selections[0].endLineNumber; + for (let i = 1, len = selections.length; i < len; i++) { + const selection = selections[i]; + minLineNumber = Math.min(minLineNumber, selection.startLineNumber); + maxLineNumber = Math.max(maxLineNumber, selection.endLineNumber); + } + this.minLineNumber = minLineNumber; + this.maxLineNumber = maxLineNumber; } } +type HorizontalRevealRequest = HorizontalRevealRangeRequest | HorizontalRevealSelectionsRequest; + export class ViewLines extends ViewPart implements IVisibleLinesHost, IViewLines { /** * Adds this amount of pixels to the right of lines (no-one wants to type near the edge of the viewport) @@ -81,6 +108,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, // --- width private _maxLineWidth: number; private readonly _asyncUpdateLineWidths: RunOnceScheduler; + private readonly _asyncCheckMonospaceFontAssumptions: RunOnceScheduler; private _horizontalRevealRequest: HorizontalRevealRequest | null; private readonly _lastRenderedData: LastRenderedData; @@ -107,7 +135,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, this._viewLineOptions = new ViewLineOptions(conf, this._context.theme.type); PartFingerprints.write(this.domNode, PartFingerprint.ViewLines); - this.domNode.setClassName('view-lines'); + this.domNode.setClassName(`view-lines ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`); Configuration.applyFontInfo(this.domNode, fontInfo); // --- width & height @@ -115,6 +143,9 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, this._asyncUpdateLineWidths = new RunOnceScheduler(() => { this._updateLineWidthsSlow(); }, 200); + this._asyncCheckMonospaceFontAssumptions = new RunOnceScheduler(() => { + this._checkMonospaceFontAssumptions(); + }, 2000); this._lastRenderedData = new LastRenderedData(); @@ -123,6 +154,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, public dispose(): void { this._asyncUpdateLineWidths.dispose(); + this._asyncCheckMonospaceFontAssumptions.dispose(); super.dispose(); } @@ -221,32 +253,36 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, public onRevealRangeRequest(e: viewEvents.ViewRevealRangeRequestEvent): boolean { // Using the future viewport here in order to handle multiple // incoming reveal range requests that might all desire to be animated - const desiredScrollTop = this._computeScrollTopToRevealRange(this._context.viewLayout.getFutureViewport(), e.source, e.range, e.verticalType); + const desiredScrollTop = this._computeScrollTopToRevealRange(this._context.viewLayout.getFutureViewport(), e.source, e.range, e.selections, e.verticalType); + + if (desiredScrollTop === -1) { + // marker to abort the reveal range request + return false; + } // validate the new desired scroll top let newScrollPosition = this._context.viewLayout.validateScrollPosition({ scrollTop: desiredScrollTop }); if (e.revealHorizontal) { - if (e.range.startLineNumber !== e.range.endLineNumber) { + if (e.range && e.range.startLineNumber !== e.range.endLineNumber) { // Two or more lines? => scroll to base (That's how you see most of the two lines) newScrollPosition = { scrollTop: newScrollPosition.scrollTop, scrollLeft: 0 }; - } else { + } else if (e.range) { // We don't necessarily know the horizontal offset of this range since the line might not be in the view... - this._horizontalRevealRequest = new HorizontalRevealRequest(e.range.startLineNumber, e.range.startColumn, e.range.endColumn, this._context.viewLayout.getCurrentScrollTop(), newScrollPosition.scrollTop, e.scrollType); + this._horizontalRevealRequest = new HorizontalRevealRangeRequest(e.range.startLineNumber, e.range.startColumn, e.range.endColumn, this._context.viewLayout.getCurrentScrollTop(), newScrollPosition.scrollTop, e.scrollType); + } else if (e.selections && e.selections.length > 0) { + this._horizontalRevealRequest = new HorizontalRevealSelectionsRequest(e.selections, this._context.viewLayout.getCurrentScrollTop(), newScrollPosition.scrollTop, e.scrollType); } } else { this._horizontalRevealRequest = null; } const scrollTopDelta = Math.abs(this._context.viewLayout.getCurrentScrollTop() - newScrollPosition.scrollTop); - if (e.scrollType === ScrollType.Smooth && scrollTopDelta > this._lineHeight) { - this._context.viewLayout.setScrollPositionSmooth(newScrollPosition); - } else { - this._context.viewLayout.setScrollPositionNow(newScrollPosition); - } + const scrollType = (scrollTopDelta <= this._lineHeight ? ScrollType.Immediate : e.scrollType); + this._context.model.setScrollPosition(newScrollPosition, scrollType); return true; } @@ -271,7 +307,7 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return this._visibleLines.onTokensChanged(e); } public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { - this._context.viewLayout.onMaxLineWidthChanged(this._maxLineWidth); + this._context.model.setMaxLineWidth(this._maxLineWidth); return this._visibleLines.onZonesChanged(e); } public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { @@ -481,6 +517,37 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return allWidthsComputed; } + private _checkMonospaceFontAssumptions(): void { + // Problems with monospace assumptions are more apparent for longer lines, + // as small rounding errors start to sum up, so we will select the longest + // line for a closer inspection + let longestLineNumber = -1; + let longestWidth = -1; + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); + for (let lineNumber = rendStartLineNumber; lineNumber <= rendEndLineNumber; lineNumber++) { + const visibleLine = this._visibleLines.getVisibleLine(lineNumber); + if (visibleLine.needsMonospaceFontCheck()) { + const lineWidth = visibleLine.getWidth(); + if (lineWidth > longestWidth) { + longestWidth = lineWidth; + longestLineNumber = lineNumber; + } + } + } + + if (longestLineNumber === -1) { + return; + } + + if (!this._visibleLines.getVisibleLine(longestLineNumber).monospaceAssumptionsAreValid()) { + for (let lineNumber = rendStartLineNumber; lineNumber <= rendEndLineNumber; lineNumber++) { + const visibleLine = this._visibleLines.getVisibleLine(lineNumber); + visibleLine.onMonospaceAssumptionsInvalidated(); + } + } + } + public prepareRender(): void { throw new Error('Not supported'); } @@ -501,13 +568,10 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, // - it might change `scrollWidth` and `scrollLeft` if (this._horizontalRevealRequest) { - const revealLineNumber = this._horizontalRevealRequest.lineNumber; - const revealStartColumn = this._horizontalRevealRequest.startColumn; - const revealEndColumn = this._horizontalRevealRequest.endColumn; - const scrollType = this._horizontalRevealRequest.scrollType; + const horizontalRevealRequest = this._horizontalRevealRequest; // Check that we have the line that contains the horizontal range in the viewport - if (viewportData.startLineNumber <= revealLineNumber && revealLineNumber <= viewportData.endLineNumber) { + if (viewportData.startLineNumber <= horizontalRevealRequest.minLineNumber && horizontalRevealRequest.maxLineNumber <= viewportData.endLineNumber) { this._horizontalRevealRequest = null; @@ -515,23 +579,17 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, this.onDidRender(); // compute new scroll position - const newScrollLeft = this._computeScrollLeftToRevealRange(revealLineNumber, revealStartColumn, revealEndColumn); - - const isViewportWrapping = this._isViewportWrapping; - if (!isViewportWrapping) { - // ensure `scrollWidth` is large enough - this._ensureMaxLineWidth(newScrollLeft.maxHorizontalOffset); - } - - // set `scrollLeft` - if (scrollType === ScrollType.Smooth) { - this._context.viewLayout.setScrollPositionSmooth({ - scrollLeft: newScrollLeft.scrollLeft - }); - } else { - this._context.viewLayout.setScrollPositionNow({ + const newScrollLeft = this._computeScrollLeftToReveal(horizontalRevealRequest); + + if (newScrollLeft) { + if (!this._isViewportWrapping) { + // ensure `scrollWidth` is large enough + this._ensureMaxLineWidth(newScrollLeft.maxHorizontalOffset); + } + // set `scrollLeft` + this._context.model.setScrollPosition({ scrollLeft: newScrollLeft.scrollLeft - }); + }, horizontalRevealRequest.scrollType); } } } @@ -542,8 +600,21 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, this._asyncUpdateLineWidths.schedule(); } + if (platform.isLinux && !this._asyncCheckMonospaceFontAssumptions.isScheduled()) { + const rendStartLineNumber = this._visibleLines.getStartLineNumber(); + const rendEndLineNumber = this._visibleLines.getEndLineNumber(); + for (let lineNumber = rendStartLineNumber; lineNumber <= rendEndLineNumber; lineNumber++) { + const visibleLine = this._visibleLines.getVisibleLine(lineNumber); + if (visibleLine.needsMonospaceFontCheck()) { + this._asyncCheckMonospaceFontAssumptions.schedule(); + break; + } + } + } + // (3) handle scrolling this._linesContent.setLayerHinting(this._canUseLayerHinting); + this._linesContent.setContain('strict'); const adjustedScrollTop = this._context.viewLayout.getCurrentScrollTop() - viewportData.bigNumbersDelta; this._linesContent.setTop(-adjustedScrollTop); this._linesContent.setLeft(-this._context.viewLayout.getCurrentScrollLeft()); @@ -555,20 +626,37 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, const iLineWidth = Math.ceil(lineWidth); if (this._maxLineWidth < iLineWidth) { this._maxLineWidth = iLineWidth; - this._context.viewLayout.onMaxLineWidthChanged(this._maxLineWidth); + this._context.model.setMaxLineWidth(this._maxLineWidth); } } - private _computeScrollTopToRevealRange(viewport: Viewport, source: string, range: Range, verticalType: viewEvents.VerticalRevealType): number { + private _computeScrollTopToRevealRange(viewport: Viewport, source: string | null | undefined, range: Range | null, selections: Selection[] | null, verticalType: viewEvents.VerticalRevealType): number { const viewportStartY = viewport.top; const viewportHeight = viewport.height; const viewportEndY = viewportStartY + viewportHeight; + let boxIsSingleRange: boolean; let boxStartY: number; let boxEndY: number; // Have a box that includes one extra line height (for the horizontal scrollbar) - boxStartY = this._context.viewLayout.getVerticalOffsetForLineNumber(range.startLineNumber); - boxEndY = this._context.viewLayout.getVerticalOffsetForLineNumber(range.endLineNumber) + this._lineHeight; + if (selections && selections.length > 0) { + let minLineNumber = selections[0].startLineNumber; + let maxLineNumber = selections[0].endLineNumber; + for (let i = 1, len = selections.length; i < len; i++) { + const selection = selections[i]; + minLineNumber = Math.min(minLineNumber, selection.startLineNumber); + maxLineNumber = Math.max(maxLineNumber, selection.endLineNumber); + } + boxIsSingleRange = false; + boxStartY = this._context.viewLayout.getVerticalOffsetForLineNumber(minLineNumber); + boxEndY = this._context.viewLayout.getVerticalOffsetForLineNumber(maxLineNumber) + this._lineHeight; + } else if (range) { + boxIsSingleRange = true; + boxStartY = this._context.viewLayout.getVerticalOffsetForLineNumber(range.startLineNumber); + boxEndY = this._context.viewLayout.getVerticalOffsetForLineNumber(range.endLineNumber) + this._lineHeight; + } else { + return -1; + } const shouldIgnoreScrollOff = source === 'mouse' && this._cursorSurroundingLinesStyle === 'default'; @@ -587,7 +675,24 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, if (boxEndY - boxStartY > viewportHeight) { // the box is larger than the viewport ... scroll to its top + if (!boxIsSingleRange) { + // do not reveal multiple cursors if there are more than fit the viewport + return -1; + } newScrollTop = boxStartY; + } else if (verticalType === viewEvents.VerticalRevealType.NearTop || verticalType === viewEvents.VerticalRevealType.NearTopIfOutsideViewport) { + if (verticalType === viewEvents.VerticalRevealType.NearTopIfOutsideViewport && viewportStartY <= boxStartY && boxEndY <= viewportEndY) { + // Box is already in the viewport... do nothing + newScrollTop = viewportStartY; + } else { + // We want a gap that is 20% of the viewport, but with a minimum of 5 lines + const desiredGapAbove = Math.max(5 * this._lineHeight, viewportHeight * 0.2); + // Try to scroll just above the box with the desired gap + const desiredScrollTop = boxStartY - desiredGapAbove; + // But ensure that the box is not pushed out of viewport + const minScrollTop = boxEndY - viewportHeight; + newScrollTop = Math.max(minScrollTop, desiredScrollTop); + } } else if (verticalType === viewEvents.VerticalRevealType.Center || verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport) { if (verticalType === viewEvents.VerticalRevealType.CenterIfOutsideViewport && viewportStartY <= boxStartY && boxEndY <= viewportEndY) { // Box is already in the viewport... do nothing @@ -604,44 +709,50 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, return newScrollTop; } - private _computeScrollLeftToRevealRange(lineNumber: number, startColumn: number, endColumn: number): { scrollLeft: number; maxHorizontalOffset: number; } { - - let maxHorizontalOffset = 0; + private _computeScrollLeftToReveal(horizontalRevealRequest: HorizontalRevealRequest): { scrollLeft: number; maxHorizontalOffset: number; } | null { const viewport = this._context.viewLayout.getCurrentViewport(); const viewportStartX = viewport.left; const viewportEndX = viewportStartX + viewport.width; - const visibleRanges = this._visibleRangesForLineRange(lineNumber, startColumn, endColumn); let boxStartX = Constants.MAX_SAFE_SMALL_INTEGER; let boxEndX = 0; - - if (!visibleRanges) { - // Unknown - return { - scrollLeft: viewportStartX, - maxHorizontalOffset: maxHorizontalOffset - }; - } - - for (const visibleRange of visibleRanges.ranges) { - if (visibleRange.left < boxStartX) { - boxStartX = visibleRange.left; + if (horizontalRevealRequest.type === 'range') { + const visibleRanges = this._visibleRangesForLineRange(horizontalRevealRequest.lineNumber, horizontalRevealRequest.startColumn, horizontalRevealRequest.endColumn); + if (!visibleRanges) { + return null; + } + for (const visibleRange of visibleRanges.ranges) { + boxStartX = Math.min(boxStartX, visibleRange.left); + boxEndX = Math.max(boxEndX, visibleRange.left + visibleRange.width); } - if (visibleRange.left + visibleRange.width > boxEndX) { - boxEndX = visibleRange.left + visibleRange.width; + } else { + for (const selection of horizontalRevealRequest.selections) { + if (selection.startLineNumber !== selection.endLineNumber) { + return null; + } + const visibleRanges = this._visibleRangesForLineRange(selection.startLineNumber, selection.startColumn, selection.endColumn); + if (!visibleRanges) { + return null; + } + for (const visibleRange of visibleRanges.ranges) { + boxStartX = Math.min(boxStartX, visibleRange.left); + boxEndX = Math.max(boxEndX, visibleRange.left + visibleRange.width); + } } } - maxHorizontalOffset = boxEndX; - boxStartX = Math.max(0, boxStartX - ViewLines.HORIZONTAL_EXTRA_PX); boxEndX += this._revealHorizontalRightPadding; + if (horizontalRevealRequest.type === 'selections' && boxEndX - boxStartX > viewport.width) { + return null; + } + const newScrollLeft = this._computeMinimumScrolling(viewportStartX, viewportEndX, boxStartX, boxEndX); return { scrollLeft: newScrollLeft, - maxHorizontalOffset: maxHorizontalOffset + maxHorizontalOffset: boxEndX }; } diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts index fc6a255eb8322..6e40faf77b47a 100644 --- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts @@ -78,6 +78,10 @@ export class LinesDecorationsOverlay extends DedupOverlay { if (linesDecorationsClassName) { r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, linesDecorationsClassName); } + const firstLineDecorationClassName = d.options.firstLineDecorationClassName; + if (firstLineDecorationClassName) { + r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.startLineNumber, firstLineDecorationClassName); + } } return r; } diff --git a/src/vs/editor/browser/viewParts/margin/margin.ts b/src/vs/editor/browser/viewParts/margin/margin.ts index 1208e3c56c9ab..8181e2f8aa807 100644 --- a/src/vs/editor/browser/viewParts/margin/margin.ts +++ b/src/vs/editor/browser/viewParts/margin/margin.ts @@ -78,6 +78,7 @@ export class Margin extends ViewPart { public render(ctx: RestrictedRenderingContext): void { this._domNode.setLayerHinting(this._canUseLayerHinting); + this._domNode.setContain('strict'); const adjustedScrollTop = ctx.scrollTop - ctx.bigNumbersDelta; this._domNode.setTop(-adjustedScrollTop); diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 5ff05c8835888..1be53aad7a380 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -8,12 +8,12 @@ import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; import { CharCode } from 'vs/base/common/charCode'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { ILine, RenderedLinesCollection } from 'vs/editor/browser/view/viewLayer'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; -import { RenderMinimap, EditorOption, MINIMAP_GUTTER_WIDTH } from 'vs/editor/common/config/editorOptions'; +import { RenderMinimap, EditorOption, MINIMAP_GUTTER_WIDTH, EditorLayoutInfoComputer } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { RGBA8 } from 'vs/editor/common/core/rgba'; import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon'; @@ -22,33 +22,18 @@ import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimap import { Constants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker'; 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 { ViewLineData } from 'vs/editor/common/viewModel/viewModel'; -import { scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, minimapSelection } from 'vs/platform/theme/common/colorRegistry'; +import { ViewLineData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; +import { minimapSelection, scrollbarShadow, minimapBackground, minimapSliderBackground, minimapSliderHoverBackground, minimapSliderActiveBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ModelDecorationMinimapOptions } from 'vs/editor/common/model/textModel'; import { Selection } from 'vs/editor/common/core/selection'; import { Color } from 'vs/base/common/color'; import { GestureEvent, EventType, Gesture } from 'vs/base/browser/touch'; import { MinimapCharRendererFactory } from 'vs/editor/browser/viewParts/minimap/minimapCharRendererFactory'; -import { MinimapPosition } from 'vs/editor/common/model'; - -function getMinimapLineHeight(renderMinimap: RenderMinimap, scale: number): number { - if (renderMinimap === RenderMinimap.Text) { - return Constants.BASE_CHAR_HEIGHT * scale; - } - // RenderMinimap.Blocks - return (Constants.BASE_CHAR_HEIGHT + 1) * scale; -} - -function getMinimapCharWidth(renderMinimap: RenderMinimap, scale: number): number { - if (renderMinimap === RenderMinimap.Text) { - return Constants.BASE_CHAR_WIDTH * scale; - } - // RenderMinimap.Blocks - return Constants.BASE_CHAR_WIDTH * scale; -} +import { MinimapPosition, TextModelResolvedOptions } from 'vs/editor/common/model'; +import { once } from 'vs/base/common/functional'; /** * The orthogonal distance to the slider at which dragging "resets". This implements "snapping" @@ -61,6 +46,10 @@ class MinimapOptions { public readonly renderMinimap: RenderMinimap; + public readonly size: 'proportional' | 'fill' | 'fit'; + + public readonly minimapHeightIsEditorHeight: boolean; + public readonly scrollBeyondLastLine: boolean; public readonly showSlider: 'always' | 'mouseover'; @@ -71,10 +60,6 @@ class MinimapOptions { public readonly lineHeight: number; - public readonly fontScale: number; - - public readonly charRenderer: MinimapCharRenderer; - /** * container dom node left position (in CSS px) */ @@ -106,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); + const minimapOpts = options.get(EditorOption.minimap); - this.renderMinimap = layoutInfo.renderMinimap | 0; + this.renderMinimap = minimapLayout.renderMinimap; + this.size = minimapOpts.size; + this.minimapHeightIsEditorHeight = minimapLayout.minimapHeightIsEditorHeight; this.scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine); - const minimapOpts = options.get(EditorOption.minimap); this.showSlider = minimapOpts.showSlider; - this.fontScale = Math.round(minimapOpts.scale * pixelRatio); - this.charRenderer = 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.max(1, Math.floor(pixelRatio * this.minimapWidth)); - this.canvasInnerHeight = Math.max(1, Math.floor(pixelRatio * this.minimapHeight)); + this.canvasInnerWidth = minimapLayout.minimapCanvasInnerWidth; + this.canvasInnerHeight = minimapLayout.minimapCanvasInnerHeight; + this.canvasOuterWidth = minimapLayout.minimapCanvasOuterWidth; + this.canvasOuterHeight = minimapLayout.minimapCanvasOuterHeight; + + 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.canvasOuterWidth = this.canvasInnerWidth / pixelRatio; - this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio; + 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 @@ -147,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) ); } } @@ -163,6 +181,7 @@ class MinimapLayout { */ public readonly scrollHeight: number; + public readonly sliderNeeded: boolean; private readonly _computedSliderRatio: number; /** @@ -186,6 +205,7 @@ class MinimapLayout { constructor( scrollTop: number, scrollHeight: number, + sliderNeeded: boolean, computedSliderRatio: number, sliderTop: number, sliderHeight: number, @@ -194,6 +214,7 @@ class MinimapLayout { ) { this.scrollTop = scrollTop; this.scrollHeight = scrollHeight; + this.sliderNeeded = sliderNeeded; this._computedSliderRatio = computedSliderRatio; this.sliderTop = sliderTop; this.sliderHeight = sliderHeight; @@ -220,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 @@ -269,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)); @@ -295,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); } } } @@ -377,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); } } @@ -444,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; @@ -452,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; @@ -461,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); @@ -503,6 +1093,7 @@ export class Minimap extends ViewPart { this._slider.setPosition('absolute'); this._slider.setClassName('minimap-slider'); this._slider.setLayerHinting(true); + this._slider.setContain('strict'); this._domNode.appendChild(this._slider); this._sliderHorizontal = createFastDomNode(document.createElement('div')); @@ -510,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()); - - this._context.privateViewEventBus.emit(new viewEvents.ViewRevealRangeRequestEvent( - 'mouse', - new Range(lineNumber, 1, lineNumber, 1), - viewEvents.VerticalRevealType.Center, - false, - ScrollType.Smooth - )); + lineNumber = Math.min(lineNumber, this._model.getLineCount()); + + this._model.revealLineNumber(lineNumber); }); this._sliderMouseMoveMonitor = new GlobalMouseMoveMonitor(); @@ -546,34 +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( - 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); } }); @@ -586,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(); @@ -604,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 { @@ -624,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'; @@ -635,121 +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._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._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._slider.setWidth(this._options.minimapWidth); + 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._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._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._model.options.minimapWidth); } - private _getBuffer(): ImageData { + private _getBuffer(): ImageData | null { if (!this._buffers) { - this._buffers = new MinimapBuffers( - this._canvas.domNode.getContext('2d')!, - this._options.canvasInnerWidth, - this._options.canvasInnerHeight, - this._tokensColorTracker.getColor(ColorId.DefaultBackground) - ); + if (this._model.options.canvasInnerWidth > 0 && this._model.options.canvasInnerHeight > 0) { + this._buffers = new MinimapBuffers( + this._canvas.domNode.getContext('2d')!, + this._model.options.canvasInnerWidth, + this._model.options.canvasInnerHeight, + this._model.options.backgroundColor + ); + } } - return this._buffers!.getBuffer(); + 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 onSelectionChanged(): boolean { + this._renderDecorations = true; + return true; } - public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { - this._selections = e.selections; + public onDecorationsChanged(): boolean { this._renderDecorations = true; return true; } - public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + 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); @@ -763,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); @@ -791,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); @@ -818,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) { @@ -849,11 +1430,16 @@ export class Minimap extends ViewPart { charWidth: number): void { const y = (lineNumber - layout.startLineNumber) * lineHeight; + // Skip rendering the line if it's vertically outside our viewport + if (y + height < 0 || y > this._model.options.canvasInnerHeight) { + return; + } + // Cache line offset data so that it is only read once per line 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); @@ -897,11 +1483,10 @@ export class Minimap extends ViewPart { canvasContext.fillRect(x, y, width, height); } - private renderLines(layout: MinimapLayout): RenderData { - const renderMinimap = this._options.renderMinimap; + private renderLines(layout: MinimapLayout): RenderData | null { 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)) { @@ -913,9 +1498,13 @@ export class Minimap extends ViewPart { // Oh well!! We need to repaint some lines... const imageData = this._getBuffer(); + if (!imageData) { + // 0 width or 0 height canvas, nothing to do + return null; + } // Render untouched lines by using last rendered data. - let [_dirtyY1, _dirtyY2, needed] = Minimap._renderUntouchedLines( + let [_dirtyY1, _dirtyY2, needed] = InnerMinimap._renderUntouchedLines( imageData, startLineNumber, endLineNumber, @@ -924,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, - this._options.charRenderer, + minimapCharWidth, + tokensColorTracker, + charRenderer, dy, + innerLinePadding, tabSize, - lineInfo.data[lineIndex]!, - this._options.fontScale + lineInfo[lineIndex]!, + fontScale, + minimapLineHeight ); } renderedLines[lineIndex] = new MinimapLine(dy); @@ -1064,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; @@ -1106,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, useLighterFont); + minimapCharRenderer.renderChar(target, dx, dy + innerLinePadding, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, force1pxHeight); } dx += charWidth; @@ -1125,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 e1136ab6c286b..c30b720c207a6 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; } @@ -32,17 +33,20 @@ export class MinimapCharRenderer { chCode: number, color: RGBA8, backgroundColor: RGBA8, - useLighterFont: boolean + fontScale: number, + 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; } const charData = useLighterFont ? this.charDataLight : this.charDataNormal; - const charIndex = getCharIndex(chCode); + const charIndex = getCharIndex(chCode, fontScale); const destWidth = target.width * Constants.RGBA_CHANNELS_CNT; @@ -58,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; @@ -78,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; } @@ -106,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 b0b91e2bca00a..46c6c8e27a992 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts @@ -5,7 +5,9 @@ import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimapCharRenderer'; 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 @@ -28,10 +30,16 @@ export class MinimapCharRendererFactory { return this.lastCreated; } - const factory = MinimapCharRendererFactory.createFromSampleData( - MinimapCharRendererFactory.createSampleData(fontFamily).data, - scale - ); + let factory: MinimapCharRenderer; + if (prebakedMiniMaps[scale]) { + factory = new MinimapCharRenderer(prebakedMiniMaps[scale](), scale); + } else { + factory = MinimapCharRendererFactory.createFromSampleData( + MinimapCharRendererFactory.createSampleData(fontFamily).data, + scale + ); + } + this.lastFontFamily = fontFamily; this.lastCreated = factory; return factory; @@ -128,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/minimap/minimapCharSheet.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharSheet.ts index af7b0eae4f8de..cda6dc9e7ee56 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharSheet.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharSheet.ts @@ -29,9 +29,13 @@ export const allCharCodes: ReadonlyArray = (() => { return v; })(); -export const getCharIndex = (chCode: number) => { +export const getCharIndex = (chCode: number, fontScale: number) => { chCode -= Constants.START_CH_CODE; if (chCode < 0 || chCode > Constants.CHAR_COUNT) { + if (fontScale <= 2) { + // for smaller scales, we can get away with using any ASCII character... + return (chCode + Constants.CHAR_COUNT) % Constants.CHAR_COUNT; + } return Constants.CHAR_COUNT - 1; // unknown symbol } diff --git a/src/vs/editor/browser/viewParts/minimap/minimapPreBaked.ts b/src/vs/editor/browser/viewParts/minimap/minimapPreBaked.ts new file mode 100644 index 0000000000000..578805adb446a --- /dev/null +++ b/src/vs/editor/browser/viewParts/minimap/minimapPreBaked.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { once } from 'vs/base/common/functional'; + +const charTable: { [hex: string]: number } = { + '0': 0, + '1': 1, + '2': 2, + '3': 3, + '4': 4, + '5': 5, + '6': 6, + '7': 7, + '8': 8, + '9': 9, + A: 10, + B: 11, + C: 12, + D: 13, + E: 14, + F: 15 +}; + +const decodeData = (str: string) => { + const output = new Uint8ClampedArray(str.length / 2); + for (let i = 0; i < str.length; i += 2) { + output[i >> 1] = (charTable[str[i]] << 4) | (charTable[str[i + 1]] & 0xF); + } + + return output; +}; + +/* +const encodeData = (data: Uint8ClampedArray, length: string) => { + const chars = '0123456789ABCDEF'; + let output = ''; + for (let i = 0; i < data.length; i++) { + output += chars[data[i] >> 4] + chars[data[i] & 0xf]; + } + return output; +}; +*/ + +/** + * Map of minimap scales to prebaked sample data at those scales. We don't + * sample much larger data, because then font family becomes visible, which + * is use-configurable. + */ +export const prebakedMiniMaps: { [scale: number]: () => Uint8ClampedArray } = { + 1: once(() => + decodeData( + '0000511D6300CF609C709645A78432005642574171487021003C451900274D35D762755E8B629C5BA856AF57BA649530C167D1512A272A3F6038604460398526BCA2A968DB6F8957C768BE5FBE2FB467CF5D8D5B795DC7625B5DFF50DE64C466DB2FC47CD860A65E9A2EB96CB54CE06DA763AB2EA26860524D3763536601005116008177A8705E53AB738E6A982F88BAA35B5F5B626D9C636B449B737E5B7B678598869A662F6B5B8542706C704C80736A607578685B70594A49715A4522E792' + ) + ), + 2: once(() => + decodeData( + '000000000000000055394F383D2800008B8B1F210002000081B1CBCBCC820000847AAF6B9AAF2119BE08B8881AD60000A44FD07DCCF107015338130C00000000385972265F390B406E2437634B4B48031B12B8A0847000001E15B29A402F0000000000004B33460B00007A752C2A0000000000004D3900000084394B82013400ABA5CFC7AD9C0302A45A3E5A98AB000089A43382D97900008BA54AA087A70A0248A6A7AE6DBE0000BF6F94987EA40A01A06DCFA7A7A9030496C32F77891D0000A99FB1A0AFA80603B29AB9CA75930D010C0948354D3900000C0948354F37460D0028BE673D8400000000AF9D7B6E00002B007AA8933400007AA642675C2700007984CFB9C3985B768772A8A6B7B20000CAAECAAFC4B700009F94A6009F840009D09F9BA4CA9C0000CC8FC76DC87F0000C991C472A2000000A894A48CA7B501079BA2C9C69BA20000B19A5D3FA89000005CA6009DA2960901B0A7F0669FB200009D009E00B7890000DAD0F5D092820000D294D4C48BD10000B5A7A4A3B1A50402CAB6CBA6A2000000B5A7A4A3B1A8044FCDADD19D9CB00000B7778F7B8AAE0803C9AB5D3F5D3F00009EA09EA0BAB006039EA0989A8C7900009B9EF4D6B7C00000A9A7816CACA80000ABAC84705D3F000096DA635CDC8C00006F486F266F263D4784006124097B00374F6D2D6D2D6D4A3A95872322000000030000000000008D8939130000000000002E22A5C9CBC70600AB25C0B5C9B400061A2DB04CA67001082AA6BEBEBFC606002321DACBC19E03087AA08B6768380000282FBAC0B8CA7A88AD25BBA5A29900004C396C5894A6000040485A6E356E9442A32CD17EADA70000B4237923628600003E2DE9C1D7B500002F25BBA5A2990000231DB6AFB4A804023025C0B5CAB588062B2CBDBEC0C706882435A75CA20000002326BD6A82A908048B4B9A5A668000002423A09CB4BB060025259C9D8A7900001C1FCAB2C7C700002A2A9387ABA200002626A4A47D6E9D14333163A0C87500004B6F9C2D643A257049364936493647358A34438355497F1A0000A24C1D590000D38DFFBDD4CD3126' + ) + ) +}; diff --git a/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts b/src/vs/editor/browser/viewParts/overlayWidgets/overlayWidgets.ts index 925ed44d28ac1..b51be8aaba8cd 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 a4e988745cbc9..287011babea04 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 { @@ -74,8 +76,14 @@ class Settings { this.right = position.right; this.domWidth = position.width; this.domHeight = position.height; - this.canvasWidth = (this.domWidth * this.pixelRatio) | 0; - this.canvasHeight = (this.domHeight * this.pixelRatio) | 0; + if (this.overviewRulerLanes === 0) { + // overview ruler is off + this.canvasWidth = 0; + this.canvasHeight = 0; + } else { + this.canvasWidth = (this.domWidth * this.pixelRatio) | 0; + this.canvasHeight = (this.domHeight * this.pixelRatio) | 0; + } const [x, w] = this._initLanes(1, this.canvasWidth, this.overviewRulerLanes); this.x = x; @@ -215,6 +223,7 @@ export class DecorationsOverviewRuler extends ViewPart { this._domNode.setClassName('decorationsOverviewRuler'); this._domNode.setPosition('absolute'); this._domNode.setLayerHinting(true); + this._domNode.setContain('strict'); this._domNode.setAttribute('aria-hidden', 'true'); this._updateSettings(false); @@ -270,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; @@ -302,6 +314,11 @@ export class DecorationsOverviewRuler extends ViewPart { } private _render(): void { + if (this._settings.overviewRulerLanes === 0) { + // overview ruler is off + this._domNode.setBackgroundColor(this._settings.backgroundColor ? this._settings.backgroundColor : ''); + return; + } const canvasWidth = this._settings.canvasWidth; const canvasHeight = this._settings.canvasHeight; const lineHeight = this._settings.lineHeight; diff --git a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts index 7b9ef090d2a5d..b75ba0ae4223c 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts @@ -26,6 +26,7 @@ export class OverviewRuler extends ViewEventHandler implements IOverviewRuler { this._domNode.setClassName(cssClassName); this._domNode.setPosition('absolute'); this._domNode.setLayerHinting(true); + this._domNode.setContain('strict'); this._zoneManager = new OverviewZoneManager((lineNumber: number) => this._context.viewLayout.getVerticalOffsetForLineNumber(lineNumber)); this._zoneManager.setDOMWidth(0); diff --git a/src/vs/editor/browser/viewParts/rulers/rulers.ts b/src/vs/editor/browser/viewParts/rulers/rulers.ts index 0f22de06a7372..696e088de5810 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 8adefd837dd44..12e4df8b4658c 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 fa2dd7220331b..cdb00570ce077 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; } @@ -369,7 +369,7 @@ export class SelectionsOverlay extends DynamicViewOverlay { private _previousFrameVisibleRangesWithStyle: (LineVisibleRangesWithStyle[] | null)[] = []; public prepareRender(ctx: RenderingContext): void { - // Build HTML for inner corners separate from HTML for the the rest of selections, + // Build HTML for inner corners separate from HTML for the rest of selections, // as the inner corner HTML can interfere with that of other selections. // In final render, make sure to place the inner corner HTML before the rest of selection HTML. See issue #77777. const output: [string, string][] = []; @@ -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 6173ba7a6fa14..7456eb657ea22 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 d77870c24a21b..484b79befe0ce 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css @@ -9,13 +9,12 @@ .monaco-editor .cursors-layer > .cursor { position: absolute; - cursor: text; overflow: hidden; } /* -- smooth-caret-animation -- */ .monaco-editor .cursors-layer.cursor-smooth-caret-animation > .cursor { - transition: 80ms; + transition: all 80ms; } /* -- block-outline-style -- */ @@ -85,4 +84,4 @@ .cursor-expand > .cursor { animation: monaco-cursor-expand 0.5s ease-in-out 0s 20 alternate; -} \ No newline at end of file +} diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index a703cbafd2be3..ef3f6e301b403 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 6d7e14730894b..a73026a3f9bdc 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -5,7 +5,7 @@ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IViewZone } from 'vs/editor/browser/editorBrowser'; +import { IViewZone, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { ViewPart } from 'vs/editor/browser/view/viewPart'; import { Position } from 'vs/editor/common/core/position'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; @@ -13,7 +13,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; - +import { IWhitespaceChangeAccessor, IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; export interface IMyViewZone { whitespaceId: string; @@ -29,6 +29,8 @@ interface IComputedViewZoneProps { minWidthInPx: number; } +const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; + export class ViewZones extends ViewPart { private _zones: { [id: string]: IMyViewZone; }; @@ -72,19 +74,26 @@ export class ViewZones extends ViewPart { // ---- begin view event handlers private _recomputeWhitespacesProps(): boolean { + const whitespaces = this._context.viewLayout.getWhitespaces(); + const oldWhitespaces = new Map(); + for (const whitespace of whitespaces) { + oldWhitespaces.set(whitespace.id, whitespace); + } let hadAChange = false; - - const keys = Object.keys(this._zones); - for (let i = 0, len = keys.length; i < len; i++) { - const id = keys[i]; - const zone = this._zones[id]; - const props = this._computeWhitespaceProps(zone.delegate); - if (this._context.viewLayout.changeWhitespace(id, props.afterViewLineNumber, props.heightInPx)) { - this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); - hadAChange = true; + 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]; + const zone = this._zones[id]; + const props = this._computeWhitespaceProps(zone.delegate); + const oldWhitespace = oldWhitespaces.get(id); + if (oldWhitespace && (oldWhitespace.afterLineNumber !== props.afterViewLineNumber || oldWhitespace.height !== props.heightInPx)) { + whitespaceAccessor.changeOneWhitespace(id, props.afterViewLineNumber, props.heightInPx); + this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); + hadAChange = true; + } } - } - + }); return hadAChange; } @@ -104,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 { @@ -138,7 +143,6 @@ export class ViewZones extends ViewPart { return 10000; } - private _computeWhitespaceProps(zone: IViewZone): IComputedViewZoneProps { if (zone.afterLineNumber === 0) { return { @@ -188,9 +192,44 @@ export class ViewZones extends ViewPart { }; } - public addZone(zone: IViewZone): string { + public changeViewZones(callback: (changeAccessor: IViewZoneChangeAccessor) => any): boolean { + let zonesHaveChanged = false; + + this._context.model.changeWhitespace((whitespaceAccessor: IWhitespaceChangeAccessor) => { + + const changeAccessor: IViewZoneChangeAccessor = { + addZone: (zone: IViewZone): string => { + zonesHaveChanged = true; + return this._addZone(whitespaceAccessor, zone); + }, + removeZone: (id: string): void => { + if (!id) { + return; + } + zonesHaveChanged = this._removeZone(whitespaceAccessor, id) || zonesHaveChanged; + }, + layoutZone: (id: string): void => { + if (!id) { + return; + } + zonesHaveChanged = this._layoutZone(whitespaceAccessor, id) || zonesHaveChanged; + } + }; + + safeInvoke1Arg(callback, changeAccessor); + + // Invalidate changeAccessor + changeAccessor.addZone = invalidFunc; + changeAccessor.removeZone = invalidFunc; + changeAccessor.layoutZone = invalidFunc; + }); + + return zonesHaveChanged; + } + + private _addZone(whitespaceAccessor: IWhitespaceChangeAccessor, zone: IViewZone): string { const props = this._computeWhitespaceProps(zone); - const whitespaceId = this._context.viewLayout.addWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx); + const whitespaceId = whitespaceAccessor.insertWhitespace(props.afterViewLineNumber, this._getZoneOrdinal(zone), props.heightInPx, props.minWidthInPx); const myZone: IMyViewZone = { whitespaceId: whitespaceId, @@ -224,11 +263,11 @@ export class ViewZones extends ViewPart { return myZone.whitespaceId; } - public removeZone(id: string): boolean { + private _removeZone(whitespaceAccessor: IWhitespaceChangeAccessor, id: string): boolean { if (this._zones.hasOwnProperty(id)) { const zone = this._zones[id]; delete this._zones[id]; - this._context.viewLayout.removeWhitespace(zone.whitespaceId); + whitespaceAccessor.removeWhitespace(zone.whitespaceId); zone.domNode.removeAttribute('monaco-visible-view-zone'); zone.domNode.removeAttribute('monaco-view-zone'); @@ -247,21 +286,20 @@ export class ViewZones extends ViewPart { return false; } - public layoutZone(id: string): boolean { - let changed = false; + private _layoutZone(whitespaceAccessor: IWhitespaceChangeAccessor, id: string): boolean { if (this._zones.hasOwnProperty(id)) { const zone = this._zones[id]; const props = this._computeWhitespaceProps(zone.delegate); // const newOrdinal = this._getZoneOrdinal(zone.delegate); - changed = this._context.viewLayout.changeWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx) || changed; + whitespaceAccessor.changeOneWhitespace(zone.whitespaceId, props.afterViewLineNumber, props.heightInPx); // TODO@Alex: change `newOrdinal` too - if (changed) { - this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); - this.setShouldRender(); - } + this._safeCallOnComputedHeight(zone.delegate, props.heightInPx); + this.setShouldRender(); + + return true; } - return changed; + return false; } public shouldSuppressMouseDownOnViewZone(id: string): boolean { @@ -365,3 +403,11 @@ export class ViewZones extends ViewPart { } } } + +function safeInvoke1Arg(func: Function, arg1: any): any { + try { + return func(arg1); + } catch (e) { + onUnexpectedError(e); + } +} diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index f387c1308e820..17539558c91ca 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 } 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'; @@ -40,7 +39,7 @@ import * as modes from 'vs/editor/common/modes'; import { editorUnnecessaryCodeBorder, editorUnnecessaryCodeOpacity } from 'vs/editor/common/view/editorColorRegistry'; import { editorErrorBorder, editorErrorForeground, editorHintBorder, editorHintForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; -import { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; +import { IEditorWhitespace } from 'vs/editor/common/viewLayout/linesLayout'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -50,11 +49,14 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; 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; -const SHOW_UNUSED_ENABLED_CLASS = 'showUnused'; - export interface ICodeEditorWidgetOptions { /** * Is this a simple widget (not a real code editor) ? @@ -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(); } } @@ -156,13 +155,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onDidType: Emitter = this._register(new Emitter()); public readonly onDidType = this._onDidType.event; - private readonly _onCompositionStart: Emitter = this._register(new Emitter()); - public readonly onCompositionStart = this._onCompositionStart.event; + private readonly _onDidCompositionStart: Emitter = this._register(new Emitter()); + public readonly onDidCompositionStart = this._onDidCompositionStart.event; - private readonly _onCompositionEnd: Emitter = this._register(new Emitter()); - public readonly onCompositionEnd = this._onCompositionEnd.event; + private readonly _onDidCompositionEnd: Emitter = this._register(new Emitter()); + public readonly onDidCompositionEnd = this._onDidCompositionEnd.event; - private readonly _onDidPaste: Emitter = this._register(new Emitter()); + private readonly _onDidPaste: Emitter = this._register(new Emitter()); public readonly onDidPaste = this._onDidPaste.event; private readonly _onMouseUp: Emitter = this._register(new Emitter()); @@ -195,6 +194,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onKeyDown: Emitter = this._register(new Emitter()); public readonly onKeyDown: Event = this._onKeyDown.event; + private readonly _onDidContentSizeChange: Emitter = this._register(new Emitter()); + public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; + private readonly _onDidScrollChange: Emitter = this._register(new Emitter()); public readonly onDidScrollChange: Event = this._onDidScrollChange.event; @@ -206,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; @@ -235,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, @@ -246,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); @@ -263,11 +269,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE const layoutInfo = options.get(EditorOption.layoutInfo); this._onDidLayoutChange.fire(layoutInfo); } - if (options.get(EditorOption.showUnused)) { - this._domElement.classList.add(SHOW_UNUSED_ENABLED_CLASS); - } else { - this._domElement.classList.remove(SHOW_UNUSED_ENABLED_CLASS); - } })); this._contextKeyService = this._register(contextKeyService.createScoped(this._domElement)); @@ -327,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); } @@ -378,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 ''; @@ -417,9 +429,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE // Current model is the new model return; } - + const hasTextFocus = this.hasTextFocus(); const detachedModel = this._detachModel(); this._attachModel(model); + if (hasTextFocus && this.hasModel()) { + this.focus(); + } const e: editorCommon.IModelChangedEvent = { oldModelUrl: detachedModel ? detachedModel.uri : null, @@ -451,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 []; @@ -513,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 { @@ -523,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, @@ -541,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 { @@ -556,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'); @@ -596,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'); @@ -613,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; @@ -654,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 { @@ -684,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'); @@ -724,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, @@ -758,7 +820,14 @@ 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 { + if (!this._modelData) { + return -1; + } + return this._modelData.viewModel.viewLayout.getContentWidth(); } public getScrollWidth(): number { @@ -774,6 +843,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.viewModel.viewLayout.getCurrentScrollLeft(); } + public getContentHeight(): number { + if (!this._modelData) { + return -1; + } + return this._modelData.viewModel.viewLayout.getContentHeight(); + } + public getScrollHeight(): number { if (!this._modelData) { return -1; @@ -787,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 { @@ -830,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, @@ -847,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 || {}; @@ -869,9 +945,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public onVisible(): void { + this._modelData?.view.refreshFocusState(); } public onHide(): void { + this._modelData?.view.refreshFocusState(); + this._focusTracker.refreshState(); } public getContribution(id: string): T { @@ -902,67 +981,110 @@ 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); + case editorCommon.Handler.ReplacePreviousChar: { + const args = >payload; + this._replacePreviousChar(source, args.text || '', args.replaceCharCnt || 0); + return; } - this._modelData.cursor.trigger(source, handlerId, payload); - if (source === 'keyboard') { - this._onDidType.fire(payload.text); + case editorCommon.Handler.Paste: { + const args = >payload; + this._paste(source, args.text || '', args.pasteOnNewLine || false, args.multicursorText || null, args.mode || null); + return; } + case editorCommon.Handler.Cut: + this._cut(source); + return; + } + + const action = this.getAction(handlerId); + if (action) { + Promise.resolve(action.run()).then(undefined, onUnexpectedError); return; } - // Special case for pasting - if (handlerId === editorCommon.Handler.Paste) { - if (!this._modelData || typeof payload.text !== 'string' || payload.text.length === 0) { - // nothing to do - 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( - new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column) - ); - } + if (!this._modelData) { return; } - if (handlerId === editorCommon.Handler.CompositionStart) { - this._onCompositionStart.fire(); + if (this._triggerEditorCommand(source, handlerId, payload)) { + return; } - if (handlerId === editorCommon.Handler.CompositionEnd) { - this._onCompositionEnd.fire(); + } + + private _startComposition(): void { + if (!this._modelData) { + return; } + this._modelData.viewModel.startComposition(); + this._onDidCompositionStart.fire(); + } - const action = this.getAction(handlerId); - if (action) { - Promise.resolve(action.run()).then(undefined, onUnexpectedError); + 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 _replacePreviousChar(source: string | null | undefined, text: string, replaceCharCnt: number): void { if (!this._modelData) { return; } + this._modelData.viewModel.replacePreviousChar(text, replaceCharCnt, source); + } - if (this._triggerEditorCommand(source, handlerId, payload)) { + 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 + }); + } + } - this._modelData.cursor.trigger(source, handlerId, payload); + private _cut(source: string | null | undefined): void { + if (!this._modelData) { + return; + } + this._modelData.viewModel.cut(source); } - private _triggerEditorCommand(source: string, handlerId: string, payload: any): boolean { + private _triggerEditorCommand(source: string | null | undefined, handlerId: string, payload: any): boolean { const command = EditorExtensionsRegistry.getEditorCommand(handlerId); if (command) { payload = payload || {}; @@ -976,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 { @@ -995,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; } @@ -1013,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 { @@ -1043,7 +1165,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData) { return null; } - return this._modelData.model.getLineDecorations(lineNumber, this._id, this._configuration.options.get(EditorOption.readOnly)); + return this._modelData.model.getLineDecorations(lineNumber, this._id, filterValidationDecorations(this._configuration.options)); } public deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[] { @@ -1147,6 +1269,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.view.createOverviewRuler(cssClassName); } + public getContainerDomNode(): HTMLElement { + return this._domElement; + } + public getDomNode(): HTMLElement | null { if (!this._modelData || !this._modelData.hasRealView) { return null; @@ -1266,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 { @@ -1312,6 +1435,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData.view.render(true, forceRedraw); } + public setAriaOptions(options: editorBrowser.IEditorAriaOptions): void { + if (!this._modelData || !this._modelData.hasRealView) { + return; + } + this._modelData.view.setAriaOptions(options); + } + public applyFontInfo(target: HTMLElement): void { Configuration.applyFontInfoSlow(target, this._configuration.options.get(EditorOption.fontInfo)); } @@ -1330,7 +1460,14 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE model.onBeforeAttached(); - const viewModel = new ViewModel(this._id, this._configuration, model, (callback) => dom.scheduleAtNextAnimationFrame(callback)); + const viewModel = new ViewModel( + this._id, + this._configuration, + model, + DOMLineBreaksComputerFactory.create(), + MonospaceLineBreaksComputerFactory.create(this._configuration.options), + (callback) => dom.scheduleAtNextAnimationFrame(callback) + ); listenersToRemove.push(model.onDidChangeDecorations((e) => this._onDidChangeModelDecorations.fire(e))); listenersToRemove.push(model.onDidChangeLanguage((e) => { @@ -1343,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(cursor.onDidReachMaxCursorCount(() => { - this._notificationService.warn(nls.localize('cursors.maximum', "The number of cursors has been limited to {0}.", Cursor.MAX_CURSOR_COUNT)); - })); - - listenersToRemove.push(cursor.onDidAttemptReadOnlyEdit(() => { - this._onDidAttemptReadOnlyEdit.fire(undefined); - })); + 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)); + } + + 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); + + 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); @@ -1399,92 +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) => { - this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText }); + 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: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null) => { - this._commandService.executeCommand(editorCommon.Handler.Paste, { - text: text, - pasteOnNewLine: pasteOnNewLine, - multicursorText: multicursorText - }); + 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); }, - type: (source: string, text: string) => { - this._commandService.executeCommand(editorCommon.Handler.Type, { - text: text - }); + type: (text: string) => { + const payload: editorCommon.TypePayload = { text }; + this._commandService.executeCommand(editorCommon.Handler.Type, payload); }, - replacePreviousChar: (source: string, text: string, replaceCharCnt: number) => { - this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, { - text: text, - replaceCharCnt: replaceCharCnt - }); + replacePreviousChar: (text: string, replaceCharCnt: number) => { + const payload: editorCommon.ReplacePreviousCharPayload = { text, replaceCharCnt }; + this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, payload); }, - 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.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]; @@ -1507,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); } @@ -1515,7 +1653,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } private _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void { - this._codeEditorService.registerDecorationType(key, options, parentTypeKey); + this._codeEditorService.registerDecorationType(key, options, parentTypeKey, this); } private _removeDecorationType(key: string): void { @@ -1572,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; @@ -1591,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); @@ -1614,6 +1757,8 @@ class EditorContextKeysManager extends Disposable { this._updateFromSelection(); this._updateFromFocus(); this._updateFromModel(); + + this._editorSimpleInput.set(this._editor.isSimpleWidget); } private _updateFromConfig(): void { @@ -1621,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 { @@ -1693,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(); @@ -1804,6 +1950,12 @@ class CodeEditorWidgetFocusTracker extends Disposable { public hasFocus(): boolean { return this._hasFocus; } + + public refreshState(): void { + if (this._domFocusTracker.refreshState) { + this._domFocusTracker.refreshState(); + } + } } const squigglyStart = encodeURIComponent(`newDecorations.zones[i]; - viewZone.suppressMouseDown = false; + viewZone.suppressMouseDown = true; let zoneId = viewChangeAccessor.addZone(viewZone); 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)); } } @@ -155,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; @@ -167,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; @@ -176,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; @@ -201,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; @@ -220,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, @@ -276,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); @@ -323,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; @@ -354,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') { @@ -373,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(); @@ -408,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; @@ -436,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 '; @@ -467,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) { @@ -497,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) { @@ -542,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; } @@ -557,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) { @@ -665,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') { @@ -683,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); } } @@ -762,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); } @@ -774,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(); } @@ -806,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); } @@ -818,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); } @@ -844,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 { @@ -871,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); } @@ -885,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) { @@ -921,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) }); } } @@ -1037,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; @@ -1048,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 = {}; @@ -1056,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'; @@ -1072,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(); } @@ -1123,11 +1191,11 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE let scrollTop = this.modifiedEditor.getScrollTop(); let scrollHeight = this.modifiedEditor.getScrollHeight(); - let computedAvailableSize = Math.max(0, layoutInfo.contentHeight); + let computedAvailableSize = Math.max(0, layoutInfo.height); let computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); let computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; - let computedSliderSize = Math.max(0, Math.floor(layoutInfo.contentHeight * computedRatio)); + let computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); let computedSliderPosition = Math.floor(scrollTop * computedRatio); return { @@ -1139,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: () => { @@ -1170,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 { @@ -1294,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); @@ -1376,12 +1444,16 @@ abstract class ViewZonesComputer { private readonly lineChanges: editorCommon.ILineChange[]; private readonly originalForeignVZ: IEditorWhitespace[]; + private readonly originalLineHeight: number; private readonly modifiedForeignVZ: IEditorWhitespace[]; + private readonly modifiedLineHeight: number; - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) { + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) { this.lineChanges = lineChanges; this.originalForeignVZ = originalForeignVZ; + this.originalLineHeight = originalLineHeight; this.modifiedForeignVZ = modifiedForeignVZ; + this.modifiedLineHeight = modifiedLineHeight; } public getViewZones(): IEditorsZones { @@ -1456,7 +1528,7 @@ abstract class ViewZonesComputer { stepOriginal.push({ afterLineNumber: viewZoneLineNumber, - heightInLines: modifiedForeignVZ.current.heightInLines, + heightInLines: modifiedForeignVZ.current.height / this.modifiedLineHeight, domNode: null, marginDomNode: marginDomNode }); @@ -1473,7 +1545,7 @@ abstract class ViewZonesComputer { } stepModified.push({ afterLineNumber: viewZoneLineNumber, - heightInLines: originalForeignVZ.current.heightInLines, + heightInLines: originalForeignVZ.current.height / this.originalLineHeight, domNode: null }); originalForeignVZ.advance(); @@ -1574,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' @@ -1606,7 +1678,7 @@ const DECORATIONS = { }), lineInsertWithSign: ModelDecorationOptions.register({ className: 'line-insert', - linesDecorationsClassName: 'insert-sign', + linesDecorationsClassName: 'insert-sign ' + diffInsertIcon.classNames, marginClassName: 'line-insert', isWholeLine: true }), @@ -1618,7 +1690,7 @@ const DECORATIONS = { }), lineDeleteWithSign: ModelDecorationOptions.register({ className: 'line-delete', - linesDecorationsClassName: 'delete-sign', + linesDecorationsClassName: 'delete-sign ' + diffRemoveIcon.classNames, marginClassName: 'line-delete', isWholeLine: true @@ -1629,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; @@ -1646,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; @@ -1732,7 +1804,7 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE } protected _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorsZones { - let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ); + let c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight)); return c.getViewZones(); } @@ -1859,8 +1931,8 @@ class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffE class SideBySideViewZonesComputer extends ViewZonesComputer { - constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]) { - super(lineChanges, originalForeignVZ, modifiedForeignVZ); + constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], originalLineHeight: number, modifiedForeignVZ: IEditorWhitespace[], modifiedLineHeight: number) { + super(lineChanges, originalForeignVZ, originalLineHeight, modifiedForeignVZ, modifiedLineHeight); } protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null { @@ -2020,7 +2092,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { private readonly renderIndicators: boolean; constructor(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean) { - super(lineChanges, originalForeignVZ, modifiedForeignVZ); + super(lineChanges, originalForeignVZ, originalEditor.getOption(EditorOption.lineHeight), modifiedForeignVZ, modifiedEditor.getOption(EditorOption.lineHeight)); this.originalModel = originalEditor.getModel()!; this.modifiedEditorOptions = modifiedEditor.getOptions(); this.modifiedEditorTabSize = modifiedEditor.getModel()!.getOptions().tabSize; @@ -2077,14 +2149,14 @@ class InlineViewZonesComputer extends ViewZonesComputer { if (this.renderIndicators) { let index = lineNumber - lineChange.originalStartLineNumber; marginHTML = marginHTML.concat([ - `
    ` + `
    ` ]); } } 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); @@ -2139,7 +2211,10 @@ class InlineViewZonesComputer extends ViewZonesComputer { lineTokens, actualDecorations, tabSize, + 0, fontInfo.spaceWidth, + fontInfo.middotWidth, + fontInfo.wsmiddotWidth, options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), options.get(EditorOption.renderControlCharacters), @@ -2154,11 +2229,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; } @@ -2202,4 +2277,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/diffNavigator.ts b/src/vs/editor/browser/widget/diffNavigator.ts index 1c941ed0facf3..f341549a1dc21 100644 --- a/src/vs/editor/browser/widget/diffNavigator.ts +++ b/src/vs/editor/browser/widget/diffNavigator.ts @@ -30,10 +30,17 @@ const defaultOptions: Options = { alwaysRevealFirst: true }; +export interface IDiffNavigator { + canNavigate(): boolean; + next(): void; + previous(): void; + dispose(): void; +} + /** * Create a new diff navigator for the provided diff editor. */ -export class DiffNavigator extends Disposable { +export class DiffNavigator extends Disposable implements IDiffNavigator { private readonly _editor: IDiffEditor; private readonly _options: Options; diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 8a915a659ff8c..e6e1c34d987dd 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 placholders 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); @@ -780,7 +799,10 @@ export class DiffReview extends Disposable { lineTokens, [], tabSize, + 0, fontInfo.spaceWidth, + fontInfo.middotWidth, + fontInfo.wsmiddotWidth, options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), options.get(EditorOption.renderControlCharacters), @@ -855,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 09a18fcf097df..5dd98c444c7e7 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 90bb8d9165a3d..7ae64d75c45ea 100644 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ b/src/vs/editor/browser/widget/inlineDiffMargin.ts @@ -9,10 +9,11 @@ import { Action } from 'vs/base/common/actions'; import { Disposable } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; 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 = '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(); @@ -150,8 +151,8 @@ export class InlineDiffMargin extends Disposable { })); - this._register(editor.onMouseMove((e: editorBrowser.IEditorMouseEvent) => { - if (e.target.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE) { + this._register(editor.onMouseMove((e: IEditorMouseEvent) => { + if (e.target.type === MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === MouseTargetType.GUTTER_VIEW_ZONE) { const viewZoneId = e.target.detail.viewZoneId; if (viewZoneId === this._viewZoneId) { @@ -165,12 +166,12 @@ export class InlineDiffMargin extends Disposable { } })); - this._register(editor.onMouseDown((e: editorBrowser.IEditorMouseEvent) => { + this._register(editor.onMouseDown((e: IEditorMouseEvent) => { if (!e.event.rightButton) { return; } - if (e.target.type === editorBrowser.MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === editorBrowser.MouseTargetType.GUTTER_VIEW_ZONE) { + if (e.target.type === MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === MouseTargetType.GUTTER_VIEW_ZONE) { const viewZoneId = e.target.detail.viewZoneId; if (viewZoneId === this._viewZoneId) { diff --git a/src/vs/editor/browser/widget/media/addition-dark.svg b/src/vs/editor/browser/widget/media/addition-dark.svg deleted file mode 100644 index 4d9389336b93e..0000000000000 --- a/src/vs/editor/browser/widget/media/addition-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/addition-light.svg b/src/vs/editor/browser/widget/media/addition-light.svg deleted file mode 100644 index 01a9de7d5abc4..0000000000000 --- a/src/vs/editor/browser/widget/media/addition-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/close-dark.svg b/src/vs/editor/browser/widget/media/close-dark.svg deleted file mode 100644 index 6d16d212ae504..0000000000000 --- a/src/vs/editor/browser/widget/media/close-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/close-light.svg b/src/vs/editor/browser/widget/media/close-light.svg deleted file mode 100644 index 742fcae4ae7d6..0000000000000 --- a/src/vs/editor/browser/widget/media/close-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/deletion-dark.svg b/src/vs/editor/browser/widget/media/deletion-dark.svg deleted file mode 100644 index 4c5a9c1e3a5d2..0000000000000 --- a/src/vs/editor/browser/widget/media/deletion-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/deletion-light.svg b/src/vs/editor/browser/widget/media/deletion-light.svg deleted file mode 100644 index d12a8ee3135d2..0000000000000 --- a/src/vs/editor/browser/widget/media/deletion-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - 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 5cd481a8108c4..0000000000000 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 d1d0e48f366df..e27e996b7b0db 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; } @@ -37,11 +32,10 @@ .monaco-diff-editor .insert-sign, .monaco-editor .delete-sign, .monaco-diff-editor .delete-sign { - background-size: 60%; - opacity: 0.7; - background-repeat: no-repeat; - background-position: 75% center; - background-size: 11px 11px; + font-size: 11px !important; + opacity: 0.7 !important; + display: flex !important; + align-items: center; } .monaco-editor.hc-black .insert-sign, .monaco-diff-editor.hc-black .insert-sign, @@ -49,27 +43,6 @@ .monaco-diff-editor.hc-black .delete-sign { opacity: 1; } -.monaco-editor .insert-sign, -.monaco-diff-editor .insert-sign { - background-image: url('addition-light.svg'); -} -.monaco-editor .delete-sign, -.monaco-diff-editor .delete-sign { - background-image: url('deletion-light.svg'); -} - -.monaco-editor.vs-dark .insert-sign, -.monaco-diff-editor.vs-dark .insert-sign, -.monaco-editor.hc-black .insert-sign, -.monaco-diff-editor.hc-black .insert-sign { - background-image: url('addition-dark.svg'); -} -.monaco-editor.vs-dark .delete-sign, -.monaco-diff-editor.vs-dark .delete-sign, -.monaco-editor.hc-black .delete-sign, -.monaco-diff-editor.hc-black .delete-sign { - background-image: url('deletion-dark.svg'); -} .monaco-editor .inline-deleted-margin-view-zone { text-align: right; @@ -78,31 +51,12 @@ 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 { display: inline-block; } -.monaco-editor .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph { - background: url('lightbulb-light.svg') center center no-repeat; -} - -.monaco-editor.vs-dark .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph, -.monaco-editor.hc-dark .margin-view-zones .inline-deleted-margin-view-zone .lightbulb-glyph { - background: url('lightbulb-dark.svg') center center no-repeat; -} - .monaco-editor .margin-view-zones .lightbulb-glyph:hover { cursor: pointer; } diff --git a/src/vs/editor/browser/widget/media/diffReview.css b/src/vs/editor/browser/widget/media/diffReview.css index f8db5bb1c67ef..56c6f15cbf6d4 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 { @@ -58,10 +59,3 @@ height: 16px; margin: 2px 0; } -.monaco-diff-editor .action-label.icon.close-diff-review { - background: url('close-light.svg') center center no-repeat; -} -.monaco-diff-editor.hc-black .action-label.icon.close-diff-review, -.monaco-diff-editor.vs-dark .action-label.icon.close-diff-review { - background: url('close-dark.svg') center center no-repeat; -} diff --git a/src/vs/editor/browser/widget/media/lightbulb-dark.svg b/src/vs/editor/browser/widget/media/lightbulb-dark.svg deleted file mode 100644 index 5fe8931a8159c..0000000000000 --- a/src/vs/editor/browser/widget/media/lightbulb-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/browser/widget/media/lightbulb-light.svg b/src/vs/editor/browser/widget/media/lightbulb-light.svg deleted file mode 100644 index 191c566fd2cb5..0000000000000 --- a/src/vs/editor/browser/widget/media/lightbulb-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/editor/common/commands/replaceCommand.ts b/src/vs/editor/common/commands/replaceCommand.ts index 4ae44cf28c154..3e628f2db760f 100644 --- a/src/vs/editor/common/commands/replaceCommand.ts +++ b/src/vs/editor/common/commands/replaceCommand.ts @@ -122,17 +122,19 @@ export class ReplaceCommandThatPreservesSelection implements ICommand { private readonly _range: Range; private readonly _text: string; private readonly _initialSelection: Selection; + private readonly _forceMoveMarkers: boolean; private _selectionId: string | null; - constructor(editRange: Range, text: string, initialSelection: Selection) { + constructor(editRange: Range, text: string, initialSelection: Selection, forceMoveMarkers: boolean = false) { this._range = editRange; this._text = text; this._initialSelection = initialSelection; + this._forceMoveMarkers = forceMoveMarkers; this._selectionId = null; } public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { - builder.addEditOperation(this._range, this._text); + builder.addTrackedEditOperation(this._range, this._text, this._forceMoveMarkers); this._selectionId = builder.trackSelection(this._initialSelection); } diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index 78a23889a2fb4..52473fd2793a6 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -11,6 +11,7 @@ import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; export interface IShiftCommandOpts { isUnshift: boolean; @@ -18,6 +19,7 @@ export interface IShiftCommandOpts { indentSize: number; insertSpaces: boolean; useTabStops: boolean; + autoIndent: EditorAutoIndentStrategy; } const repeatCache: { [str: string]: string[]; } = Object.create(null); @@ -101,17 +103,17 @@ 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; + } } - } - if (this._opts.useTabStops) { // keep track of previous line's "miss-alignment" let previousLineExtraSpaces = 0, extraSpaces = 0; - for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++ , previousLineExtraSpaces = extraSpaces) { + for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++, previousLineExtraSpaces = extraSpaces) { extraSpaces = 0; let lineText = model.getLineContent(lineNumber); let indentationEndIndex = strings.firstNonWhitespaceIndex(lineText); @@ -137,7 +139,7 @@ export class ShiftCommand implements ICommand { // The current line is "miss-aligned", so let's see if this is expected... // This can only happen when it has trailing commas in the indent if (model.isCheapToTokenize(lineNumber - 1)) { - let enterAction = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)); + let enterAction = LanguageConfigurationRegistry.getEnterAction(this._opts.autoIndent, model, new Range(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1), lineNumber - 1, model.getLineMaxColumn(lineNumber - 1))); if (enterAction) { extraSpaces = previousLineExtraSpaces; if (enterAction.appendText) { @@ -186,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 c7f50efd2d9cf..3bde5dfd839a3 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -8,10 +8,10 @@ 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 * as editorCommon from 'vs/editor/common/editorCommon'; +import { IConfiguration, IDimension } from 'vs/editor/common/editorCommon'; import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; @@ -256,6 +256,20 @@ function migrateOptions(options: IEditorOptions): void { enabled: false }; } + + const autoIndent = options.autoIndent; + if (autoIndent === true) { + options.autoIndent = 'full'; + } else if (autoIndent === false) { + options.autoIndent = 'advanced'; + } + + const matchBrackets = options.matchBrackets; + if (matchBrackets === true) { + options.matchBrackets = 'always'; + } else if (matchBrackets === false) { + options.matchBrackets = 'never'; + } } function deepCloneAndMigrateOptions(_options: IEditorOptions): IEditorOptions { @@ -264,15 +278,20 @@ function deepCloneAndMigrateOptions(_options: IEditorOptions): IEditorOptions { return options; } -export abstract class CommonEditorConfiguration extends Disposable implements editorCommon.IConfiguration { +export abstract class CommonEditorConfiguration extends Disposable implements IConfiguration { 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; @@ -284,6 +303,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed this.isSimpleWidget = isSimpleWidget; this._isDominatedByLongLines = false; + this._computeOptionsMemory = new ComputeOptionsMemory(); + this._viewLineCount = 1; this._lineNumbersDigitCount = 1; this._rawOptions = deepCloneAndMigrateOptions(_options); @@ -294,7 +315,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed this._register(TabFocus.onDidChangeTabFocus(_ => this._recomputeOptions())); } - public observeReferenceElement(dimension?: editorCommon.IDimension): void { + public observeReferenceElement(dimension?: IDimension): void { } public dispose(): void { @@ -316,6 +337,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed } this.options = newOptions; + this._onDidChangeFast.fire(changeEvent); this._onDidChange.fire(changeEvent); } } @@ -328,11 +350,13 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed 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, @@ -357,7 +381,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed } continue; } - if (typeof baseValue === 'object' && typeof subsetValue === 'object') { + if (baseValue && typeof baseValue === 'object' && subsetValue && typeof subsetValue === 'object') { if (!this._subsetEquals(baseValue, subsetValue)) { return false; } @@ -391,11 +415,19 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed } 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 = lineNumbersDigitCount; + this._recomputeOptions(); + } + + public setViewLineCount(viewLineCount: number): void { + if (this._viewLineCount === viewLineCount) { return; } - this._lineNumbersDigitCount = digitCount; + this._viewLineCount = viewLineCount; this._recomputeOptions(); } @@ -413,14 +445,17 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed } -const configurationRegistry = Registry.as(Extensions.Configuration); -const editorConfiguration: IConfigurationNode = { +export const editorConfigurationBaseNode = Object.freeze({ id: 'editor', order: 5, type: 'object', title: nls.localize('editorConfigurationTitle', "Editor"), - overridable: true, - scope: ConfigurationScope.RESOURCE, + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, +}); + +const configurationRegistry = Registry.as(Extensions.Configuration); +const editorConfiguration: IConfigurationNode = { + ...editorConfigurationBaseNode, properties: { 'editor.tabSize': { type: 'number', @@ -467,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, @@ -490,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 1920af832ea8a..ec9fa7c336eee 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -11,7 +11,7 @@ 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 @@ -30,6 +30,17 @@ export type EditorAutoSurroundStrategy = 'languageDefined' | 'quotes' | 'bracket */ export type EditorAutoClosingOvertypeStrategy = 'always' | 'auto' | 'never'; +/** + * Configuration options for auto indentation in the editor + */ +export const enum EditorAutoIndentStrategy { + None = 0, + Keep = 1, + Brackets = 2, + Advanced = 3, + Full = 4 +} + /** * Configuration options for the editor. */ @@ -46,7 +57,7 @@ export interface IEditorOptions { * 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 `~!@#$%^&*()-=+[{]}\\|;:\'",.<>/? @@ -62,7 +73,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; /** @@ -81,6 +92,11 @@ export interface IEditorOptions { * Defaults to true. */ renderFinalNewline?: boolean; + /** + * Remove unusual line terminators like LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS). + * Defaults to 'prompt'. + */ + unusualLineTerminators?: 'off' | 'prompt' | 'auto'; /** * Should the corresponding line be selected when clicking on the line number? * Defaults to true. @@ -123,6 +139,16 @@ 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. + */ + renderValidationDecorations?: 'editable' | 'on' | 'off'; /** * Control the behavior and rendering of the scrollbars. */ @@ -185,8 +211,8 @@ export interface IEditorOptions { */ fontLigatures?: boolean | string; /** - * Disable the use of `will-change` for the editor margin and lines layers. - * The usage of `will-change` acts as a hint for browsers to create an extra layer. + * Disable the use of `transform: translate3d(0px, 0px, 0px)` for the editor margin and lines layers. + * The usage of `transform: translate3d(0px, 0px, 0px)` acts as a hint for browsers to create an extra layer. * Defaults to false. */ disableLayerHinting?: boolean; @@ -249,21 +275,21 @@ export interface IEditorOptions { * Defaults to 'same' in vscode and to 'none' in monaco-editor. */ wrappingIndent?: 'none' | 'same' | 'indent' | 'deepIndent'; + /** + * Controls the wrapping strategy to use. + * Defaults to 'simple'. + */ + wrappingStrategy?: 'simple' | 'advanced'; /** * Configure word wrapping characters. A break will be introduced before these characters. - * Defaults to '{([+'. + * Defaults to '([{‘“〈《「『【〔([{「£¥$£¥++'. */ wordWrapBreakBeforeCharacters?: string; /** * Configure word wrapping characters. A break will be introduced after these characters. - * Defaults to ' \t})]?|&,;'. + * Defaults to ' \t})]?|/&.,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」'. */ wordWrapBreakAfterCharacters?: string; - /** - * Configure word wrapping characters. A break will be introduced after these characters only if no `wordWrapBreakBeforeCharacters` or `wordWrapBreakAfterCharacters` were found. - * Defaults to '.'. - */ - wordWrapBreakObtrusiveCharacters?: string; /** * Performance guard: Stop rendering a line after x characters. * Defaults to 10000. @@ -283,6 +309,10 @@ export interface IEditorOptions { * Enable inline color decorators and color picker rendering. */ colorDecorators?: boolean; + /** + * Control the behaviour of comments in the editor. + */ + comments?: IEditorCommentsOptions; /** * Enable custom contextmenu. * Defaults to true. @@ -298,6 +328,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' @@ -318,6 +358,10 @@ export interface IEditorOptions { * Defaults to 'auto'. It is best to leave this to 'auto'. */ accessibilitySupport?: 'auto' | 'off' | 'on'; + /** + * Controls the number of lines in the editor that can be read out by a screen reader + */ + accessibilityPageSize?: number; /** * Suggest options. */ @@ -336,6 +380,10 @@ export interface IEditorOptions { * Defaults to 10 (ms) */ quickSuggestionsDelay?: number; + /** + * Controls the spacing around the editor. + */ + padding?: IEditorPaddingOptions; /** * Parameter hint options. */ @@ -360,10 +408,10 @@ 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?: boolean; + autoIndent?: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'; /** * Enable format on type. * Defaults to false. @@ -448,7 +496,7 @@ export interface IEditorOptions { */ codeActionsOnSaveTimeout?: number; /** - * Enable code folding + * Enable code folding. * Defaults to true. */ folding?: boolean; @@ -457,21 +505,31 @@ export interface IEditorOptions { * Defaults to 'auto'. */ foldingStrategy?: 'auto' | 'indentation'; + /** + * Enable highlight for folded regions. + * Defaults to true. + */ + foldingHighlight?: boolean; /** * Controls whether the fold actions in the gutter stay always visible or hide unless the mouse is over the gutter. * 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 true. + * Defaults to 'always'. */ - matchBrackets?: boolean; + matchBrackets?: 'never' | 'near' | 'always'; /** * 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. @@ -492,6 +550,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. */ @@ -520,13 +583,20 @@ export interface IEditorOptions { * Controls fading out of unused variables. */ showUnused?: boolean; -} - -export interface IEditorConstructionOptions extends IEditorOptions { /** - * The initial editor dimension (to avoid measuring the container). + * Controls whether to focus the inline editor in the peek widget by default. + * Defaults to false. + */ + peekWidgetDefaultFocus?: 'tree' | 'editor'; + /** + * Controls whether the definition link opens element in the peek widget. + * Defaults to false. */ - dimension?: IDimension; + definitionLinkOpensInPeek?: boolean; + /** + * Controls strikethrough deprecated variables. + */ + showDeprecated?: boolean; } /** @@ -569,6 +639,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 @@ -584,9 +664,6 @@ export class ConfigurationChangedEvent { constructor(values: boolean[]) { this._values = values; } - /** - * @internal - */ public hasChanged(id: EditorOption): boolean { return this._values[id]; } @@ -609,7 +686,7 @@ export class ValidatedEditorOptions { } /** - * @internal + * All computed editor options. */ export interface IComputedEditorOptions { get(id: T): FindComputedEditorOptionValueById; @@ -621,11 +698,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; @@ -636,10 +715,23 @@ export interface IEnvironmentalOptions { /** * @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; - readonly defaultValue: V; + defaultValue: V; /** * @internal */ @@ -760,15 +852,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); @@ -914,6 +1004,20 @@ class EditorEnumOption extends Bas //#endregion +//#region autoIndent + +function _autoIndentFromString(autoIndent: 'none' | 'keep' | 'brackets' | 'advanced' | 'full'): EditorAutoIndentStrategy { + switch (autoIndent) { + case 'none': return EditorAutoIndentStrategy.None; + case 'keep': return EditorAutoIndentStrategy.Keep; + case 'brackets': return EditorAutoIndentStrategy.Brackets; + case 'advanced': return EditorAutoIndentStrategy.Advanced; + case 'full': return EditorAutoIndentStrategy.Full; + } +} + +//#endregion + //#region accessibilitySupport class EditorAccessibilitySupport extends BaseEditorOption { @@ -955,6 +1059,64 @@ class EditorAccessibilitySupport extends BaseEditorOption>; + +class EditorComments extends BaseEditorOption { + + constructor() { + const defaults: EditorCommentsOptions = { + insertSpace: true, + ignoreEmptyLines: true, + }; + super( + EditorOption.comments, 'comments', defaults, + { + 'editor.comments.insertSpace': { + type: 'boolean', + default: defaults.insertSpace, + description: nls.localize('comments.insertSpace', "Controls whether a space character is inserted when commenting.") + }, + 'editor.comments.ignoreEmptyLines': { + type: 'boolean', + default: defaults.ignoreEmptyLines, + description: nls.localize('comments.ignoreEmptyLines', 'Controls if empty lines should be ignored with toggle, add or remove actions for line comments.') + }, + } + ); + } + + public validate(_input: any): EditorCommentsOptions { + if (!_input || typeof _input !== 'object') { + return this.defaultValue; + } + const input = _input as IEditorCommentsOptions; + return { + insertSpace: EditorBooleanOption.boolean(input.insertSpace, this.defaultValue.insertSpace), + ignoreEmptyLines: EditorBooleanOption.boolean(input.ignoreEmptyLines, this.defaultValue.ignoreEmptyLines), + }; + } +} + +//#endregion + //#region cursorBlinking /** @@ -1067,19 +1229,28 @@ class EditorClassName extends ComputedEditorOption>; @@ -1134,14 +1313,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, @@ -1156,7 +1342,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', @@ -1168,23 +1354,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), }; } } @@ -1215,7 +1409,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'; @@ -1299,15 +1535,20 @@ export type GoToLocationValues = 'peek' | 'gotoAndPeek' | 'goto'; * Configuration options for go to location */ export interface IGotoLocationOptions { - /** - * Control how goto-command work when having multiple results. - */ + multiple?: GoToLocationValues; + multipleDefinitions?: GoToLocationValues; multipleTypeDefinitions?: GoToLocationValues; multipleDeclarations?: GoToLocationValues; - multipleImplemenations?: GoToLocationValues; + multipleImplementations?: GoToLocationValues; multipleReferences?: GoToLocationValues; + + alternativeDefinitionCommand?: string; + alternativeTypeDefinitionCommand?: string; + alternativeDeclarationCommand?: string; + alternativeImplementationCommand?: string; + alternativeReferenceCommand?: string; } export type GoToLocationOptions = Readonly>; @@ -1320,10 +1561,15 @@ class EditorGoToLocation extends BaseEditorOption(input.multipleDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleTypeDefinitions: input.multipleTypeDefinitions ?? EditorStringEnumOption.stringSet(input.multipleTypeDefinitions, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleDeclarations: input.multipleDeclarations ?? EditorStringEnumOption.stringSet(input.multipleDeclarations, 'peek', ['peek', 'gotoAndPeek', 'goto']), - multipleImplemenations: input.multipleImplemenations ?? EditorStringEnumOption.stringSet(input.multipleImplemenations, 'peek', ['peek', 'gotoAndPeek', 'goto']), + multipleImplementations: input.multipleImplementations ?? EditorStringEnumOption.stringSet(input.multipleImplementations, 'peek', ['peek', 'gotoAndPeek', 'goto']), multipleReferences: input.multipleReferences ?? EditorStringEnumOption.stringSet(input.multipleReferences, 'peek', ['peek', 'gotoAndPeek', 'goto']), + alternativeDefinitionCommand: EditorStringOption.string(input.alternativeDefinitionCommand, this.defaultValue.alternativeDefinitionCommand), + alternativeTypeDefinitionCommand: EditorStringOption.string(input.alternativeTypeDefinitionCommand, this.defaultValue.alternativeTypeDefinitionCommand), + alternativeDeclarationCommand: EditorStringOption.string(input.alternativeDeclarationCommand, this.defaultValue.alternativeDeclarationCommand), + alternativeImplementationCommand: EditorStringOption.string(input.alternativeImplementationCommand, this.defaultValue.alternativeImplementationCommand), + alternativeReferenceCommand: EditorStringOption.string(input.alternativeReferenceCommand, this.defaultValue.alternativeReferenceCommand), }; } } @@ -1437,7 +1713,7 @@ class EditorHover extends BaseEditorOption>; + 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; } /** @@ -1602,15 +1920,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; @@ -1626,22 +2133,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); @@ -1675,83 +2185,86 @@ 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; + let isWordWrapMinified = false; + let isViewportWrapping = false; + let wrappingColumn = -1; + + 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, glyphMarginLeft: glyphMarginLeft, glyphMarginWidth: glyphMarginWidth, - glyphMarginHeight: outerHeight, lineNumbersLeft: lineNumbersLeft, lineNumbersWidth: lineNumbersWidth, - lineNumbersHeight: outerHeight, decorationsLeft: decorationsLeft, decorationsWidth: lineDecorationsWidth, - decorationsHeight: outerHeight, contentLeft: contentLeft, contentWidth: contentWidth, - contentHeight: outerHeight, - renderMinimap: renderMinimap, - minimapLeft: minimapLeft, - minimapWidth: minimapWidth, + minimap: minimapLayout, viewportColumn: viewportColumn, + isWordWrapMinified: isWordWrapMinified, + isViewportWrapping: isViewportWrapping, + wrappingColumn: wrappingColumn, + verticalScrollbarWidth: verticalScrollbarWidth, horizontalScrollbarHeight: horizontalScrollbarHeight, @@ -1799,7 +2312,7 @@ class EditorLightbulb extends BaseEditorOption(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), @@ -1954,6 +2485,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 /** @@ -1999,7 +2589,7 @@ class EditorParameterHints extends BaseEditorOption>; @@ -2089,7 +2679,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.") @@ -2194,13 +2820,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(); + rulers.sort((a, b) => a.column - b.column); return rulers; } return this.defaultValue; @@ -2250,6 +2887,11 @@ export interface IEditorScrollbarOptions { * Defaults to true. */ handleMouseWheel?: boolean; + /** + * Always consume mouse wheel events (always call preventDefault() and stopPropagation() on the browser events). + * Defaults to true. + */ + alwaysConsumeMouseWheel?: boolean; /** * Height in pixels for the horizontal scrollbar. * Defaults to 10 (px). @@ -2280,6 +2922,7 @@ export interface InternalEditorScrollbarOptions { readonly verticalHasArrows: boolean; readonly horizontalHasArrows: boolean; readonly handleMouseWheel: boolean; + readonly alwaysConsumeMouseWheel: boolean; readonly horizontalScrollbarSize: number; readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; @@ -2309,17 +2952,18 @@ class EditorScrollbar extends BaseEditorOption>; @@ -2485,7 +3147,7 @@ class EditorSuggest extends BaseEditorOption { 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, }; } } @@ -2911,13 +3553,11 @@ function register(option: IEditorOption): IEd return option; } -/** - * @internal - */ export const enum EditorOption { acceptSuggestionOnCommitCharacter, acceptSuggestionOnEnter, accessibilitySupport, + accessibilityPageSize, ariaLabel, autoClosingBrackets, autoClosingOvertype, @@ -2927,6 +3567,8 @@ export const enum EditorOption { autoSurround, codeLens, colorDecorators, + columnSelection, + comments, contextmenu, copyWithSyntaxHighlighting, cursorBlinking, @@ -2945,6 +3587,8 @@ export const enum EditorOption { fixedOverflowWidgets, folding, foldingStrategy, + foldingHighlight, + unfoldOnClickAfterEndOfLine, fontFamily, fontInfo, fontLigatures, @@ -2976,14 +3620,20 @@ export const enum EditorOption { occurrencesHighlight, overviewRulerBorder, overviewRulerLanes, + padding, parameterHints, + peekWidgetDefaultFocus, + definitionLinkOpensInPeek, quickSuggestions, quickSuggestionsDelay, readOnly, + renameOnType, renderControlCharacters, renderIndentGuides, renderFinalNewline, renderLineHighlight, + renderLineHighlightOnlyWhenFocus, + renderValidationDecorations, renderWhitespace, revealHorizontalRightPadding, roundedSelection, @@ -2991,6 +3641,7 @@ export const enum EditorOption { scrollbar, scrollBeyondLastColumn, scrollBeyondLastLine, + scrollPredominantAxis, selectionClipboard, selectionHighlight, selectOnLineNumbers, @@ -3005,15 +3656,17 @@ export const enum EditorOption { suggestOnTriggerCharacters, suggestSelection, tabCompletion, + unusualLineTerminators, useTabStops, wordSeparators, wordWrap, wordWrapBreakAfterCharacters, wordWrapBreakBeforeCharacters, - wordWrapBreakObtrusiveCharacters, wordWrapColumn, wordWrapMinified, wrappingIndent, + wrappingStrategy, + showDeprecated, // Leave these at the end (because they have dependencies!) editorClassName, @@ -3024,7 +3677,19 @@ export const enum EditorOption { } /** - * @internal + * WORKAROUND: TS emits "any" for complex editor options values (anything except string, bool, enum, etc. ends up being "any") + * @monacodtsreplace + * /accessibilitySupport, any/accessibilitySupport, AccessibilitySupport/ + * /comments, any/comments, EditorCommentsOptions/ + * /find, any/find, EditorFindOptions/ + * /fontInfo, any/fontInfo, FontInfo/ + * /gotoLocation, any/gotoLocation, GoToLocationOptions/ + * /hover, any/hover, EditorHoverOptions/ + * /lightbulb, any/lightbulb, EditorLightbulbOptions/ + * /minimap, any/minimap, EditorMinimapOptions/ + * /parameterHints, any/parameterHints, InternalParameterHintOptions/ + * /quickSuggestions, any/quickSuggestions, ValidQuickSuggestionsOptions/ + * /suggest, any/suggest, InternalSuggestOptions/ */ export const EditorOptions = { acceptSuggestionOnCommitCharacter: register(new EditorBooleanOption( @@ -3045,6 +3710,8 @@ export const EditorOptions = { } )), accessibilitySupport: register(new EditorAccessibilitySupport()), + accessibilityPageSize: register(new EditorIntOption(EditorOption.accessibilityPageSize, 'accessibilityPageSize', 10, 1, Constants.MAX_SAFE_SMALL_INTEGER, + { description: nls.localize('accessibilityPageSize', "Controls the number of lines in the editor that can be read out by a screen reader. Warning: this has a performance implication for numbers larger than the default.") })), ariaLabel: register(new EditorStringOption( EditorOption.ariaLabel, 'ariaLabel', nls.localize('editorViewAccessibleLabel', "Editor content") )), @@ -3089,9 +3756,21 @@ export const EditorOptions = { description: nls.localize('autoClosingQuotes', "Controls whether the editor should automatically close quotes after the user adds an opening quote.") } )), - autoIndent: register(new EditorBooleanOption( - EditorOption.autoIndent, 'autoIndent', true, - { description: nls.localize('autoIndent', "Controls whether the editor should automatically adjust the indentation when users type, paste or move lines. Extensions with indentation rules of the language must be available.") } + autoIndent: register(new EditorEnumOption( + EditorOption.autoIndent, 'autoIndent', + EditorAutoIndentStrategy.Full, 'full', + ['none', 'keep', 'brackets', 'advanced', 'full'], + _autoIndentFromString, + { + enumDescriptions: [ + nls.localize('editor.autoIndent.none', "The editor will not insert indentation automatically."), + nls.localize('editor.autoIndent.keep', "The editor will keep the current line's indentation."), + nls.localize('editor.autoIndent.brackets', "The editor will keep the current line's indentation and honor language defined brackets."), + nls.localize('editor.autoIndent.advanced', "The editor will keep the current line's indentation, honor language defined brackets and invoke special onEnterRules defined by languages."), + nls.localize('editor.autoIndent.full', "The editor will keep the current line's indentation, honor language defined brackets, invoke special onEnterRules defined by languages, and honor indentationRules defined by languages."), + ], + description: nls.localize('autoIndent', "Controls whether the editor should automatically adjust the indentation when users type, paste, move or indent lines.") + } )), automaticLayout: register(new EditorBooleanOption( EditorOption.automaticLayout, 'automaticLayout', false, @@ -3118,6 +3797,11 @@ 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, )), @@ -3154,10 +3838,10 @@ export const EditorOptions = { ['default', 'all'] as const, { enumDescriptions: [ - nls.localize('cursorSurroundingLinesStyle.default', "`cursorSurroundingLines` is enforced only when triggered from keyboard and api"), + nls.localize('cursorSurroundingLinesStyle.default', "`cursorSurroundingLines` is enforced only when triggered via the keyboard or API."), nls.localize('cursorSurroundingLinesStyle.all', "`cursorSurroundingLines` is enforced always.") ], - description: nls.localize('cursorSurroundingLinesStyle', "Controls when `cursorSurroundingLines` should be enforced") + description: nls.localize('cursorSurroundingLinesStyle', "Controls when `cursorSurroundingLines` should be enforced.") } )), cursorWidth: register(new EditorIntOption( @@ -3196,7 +3880,21 @@ 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, @@ -3205,13 +3903,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.") } @@ -3248,15 +3940,17 @@ export const EditorOptions = { lineNumbers: register(new EditorRenderLineNumbersOption()), lineNumbersMinChars: register(new EditorIntOption( EditorOption.lineNumbersMinChars, 'lineNumbersMinChars', - 5, 1, 10 + 5, 1, 300 )), links: register(new EditorBooleanOption( EditorOption.links, 'links', true, { description: nls.localize('links', "Controls whether the editor should detect links and make them clickable.") } )), - matchBrackets: register(new EditorBooleanOption( - EditorOption.matchBrackets, 'matchBrackets', true, - { description: nls.localize('matchBrackets', "Highlight matching brackets when one of them is selected.") } + matchBrackets: register(new EditorStringEnumOption( + EditorOption.matchBrackets, 'matchBrackets', + 'always' as 'never' | 'near' | 'always', + ['always', 'near', 'never'] as const, + { description: nls.localize('matchBrackets', "Highlight matching brackets.") } )), minimap: register(new EditorMinimap()), mouseStyle: register(new EditorStringEnumOption( @@ -3320,7 +4014,24 @@ export const EditorOptions = { EditorOption.overviewRulerLanes, 'overviewRulerLanes', 3, 0, 3 )), + padding: register(new EditorPadding()), parameterHints: register(new EditorParameterHints()), + peekWidgetDefaultFocus: register(new EditorStringEnumOption( + EditorOption.peekWidgetDefaultFocus, 'peekWidgetDefaultFocus', + 'tree' as 'tree' | 'editor', + ['tree', 'editor'] as const, + { + enumDescriptions: [ + 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', @@ -3330,6 +4041,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.") } @@ -3356,15 +4071,25 @@ 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', + ['editable', 'on', 'off'] as const + )), 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.") @@ -3389,6 +4114,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, { @@ -3407,12 +4136,22 @@ export const EditorOptions = { 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', @@ -3476,6 +4215,19 @@ export const EditorOptions = { description: nls.localize('tabCompletion', "Enables tab completions.") } )), + unusualLineTerminators: register(new EditorStringEnumOption( + EditorOption.unusualLineTerminators, 'unusualLineTerminators', + 'prompt' as 'off' | 'prompt' | 'auto', + ['off', 'prompt', 'auto'] as const, + { + enumDescriptions: [ + nls.localize('unusualLineTerminators.off', "Unusual line terminators are ignored."), + nls.localize('unusualLineTerminators.prompt', "Unusual line terminators prompt to be removed."), + nls.localize('unusualLineTerminators.auto', "Unusual line terminators are automatically 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.") } @@ -3517,16 +4269,12 @@ export const EditorOptions = { )), wordWrapBreakAfterCharacters: register(new EditorStringOption( EditorOption.wordWrapBreakAfterCharacters, 'wordWrapBreakAfterCharacters', - ' \t})]?|/&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', + ' \t})]?|/&.,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」', )), wordWrapBreakBeforeCharacters: register(new EditorStringOption( EditorOption.wordWrapBreakBeforeCharacters, 'wordWrapBreakBeforeCharacters', '([{‘“〈《「『【〔([{「£¥$£¥++' )), - wordWrapBreakObtrusiveCharacters: register(new EditorStringOption( - EditorOption.wordWrapBreakObtrusiveCharacters, 'wordWrapBreakObtrusiveCharacters', - '.' - )), wordWrapColumn: register(new EditorIntOption( EditorOption.wordWrapColumn, 'wordWrapColumn', 80, 1, Constants.MAX_SAFE_SMALL_INTEGER, @@ -3558,28 +4306,28 @@ export const EditorOptions = { description: nls.localize('wrappingIndent', "Controls the indentation of wrapped lines."), } )), + wrappingStrategy: register(new EditorStringEnumOption( + EditorOption.wrappingStrategy, 'wrappingStrategy', + 'simple' as 'simple' | 'advanced', + ['simple', 'advanced'] as const, + { + enumDescriptions: [ + 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('wrappingStrategy', "Controls the algorithm that computes wrapping points.") + } + )), // Leave these at the end (because they have dependencies!) editorClassName: register(new EditorClassName()), pixelRatio: register(new EditorPixelRatio()), tabFocusMode: register(new EditorTabFocusMode()), layoutInfo: register(new EditorLayoutInfoComputer()), - wrappingInfo: register(new EditorWrappingInfoComputer()), + wrappingInfo: register(new EditorWrappingInfoComputer()) }; -/** - * @internal - */ type EditorOptionsType = typeof EditorOptions; -/** - * @internal - */ type FindEditorOptionsKeyById = { [K in keyof EditorOptionsType]: EditorOptionsType[K]['id'] extends T ? K : never }[keyof EditorOptionsType]; -/** - * @internal - */ type ComputedEditorOptionValue> = T extends IEditorOption ? R : never; -/** - * @internal - */ export type FindComputedEditorOptionValueById = NonNullable]>>; diff --git a/src/vs/editor/common/config/fontInfo.ts b/src/vs/editor/common/config/fontInfo.ts index 16719602fa143..e5d21cb623a9b 100644 --- a/src/vs/editor/common/config/fontInfo.ts +++ b/src/vs/editor/common/config/fontInfo.ts @@ -134,6 +134,8 @@ export class FontInfo extends BareFontInfo { readonly typicalFullwidthCharacterWidth: number; readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; + readonly middotWidth: number; + readonly wsmiddotWidth: number; readonly maxDigitWidth: number; /** @@ -152,6 +154,8 @@ export class FontInfo extends BareFontInfo { typicalFullwidthCharacterWidth: number; canUseHalfwidthRightwardsArrow: boolean; spaceWidth: number; + middotWidth: number; + wsmiddotWidth: number; maxDigitWidth: number; }, isTrusted: boolean) { super(opts); @@ -161,6 +165,8 @@ export class FontInfo extends BareFontInfo { this.typicalFullwidthCharacterWidth = opts.typicalFullwidthCharacterWidth; this.canUseHalfwidthRightwardsArrow = opts.canUseHalfwidthRightwardsArrow; this.spaceWidth = opts.spaceWidth; + this.middotWidth = opts.middotWidth; + this.wsmiddotWidth = opts.wsmiddotWidth; this.maxDigitWidth = opts.maxDigitWidth; } @@ -179,6 +185,8 @@ export class FontInfo extends BareFontInfo { && this.typicalFullwidthCharacterWidth === other.typicalFullwidthCharacterWidth && 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 80e20c21d7f6f..7a966ed223ec6 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)); - } 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(eventsCollector, source, cursorChangeReason, oldState, false)) { + this._revealPrimaryCursor(eventsCollector, source, VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); } + } - this._validateAutoClosedActions(); + public setIsDoingComposition(isDoingComposition: boolean): void { + this._isDoingComposition = isDoingComposition; + } - if (this._emitStateChangedIfNecessary(source, cursorChangeReason, oldState)) { - this._revealRange(source, RevealTarget.Primary, viewEvents.VerticalRevealType.Simple, true, editorCommon.ScrollType.Smooth); - } + public startComposition(eventsCollector: ViewModelEventsCollector): void { + this._selectionsWhenCompositionStarted = this.getSelections().slice(0); } - 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 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.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters)); + this._selectionsWhenCompositionStarted = null; + } + }, eventsCollector, source); } - 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 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); + 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._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), autoClosedCharacters, chr)); + // 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; - } + offset += charLength; + } - } else { - this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections(), text)); - } + } else { + this._executeEditOperation(TypeOperations.typeWithoutInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._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 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 _paste(text: string, pasteOnNewLine: boolean, multicursorText: string[]): void { - this._executeEditOperation(TypeOperations.paste(this.context.config, this.context.model, this.getSelections(), text, pasteOnNewLine, multicursorText)); + 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 _cut(): void { - this._executeEditOperation(DeleteOperations.cut(this.context.config, this.context.model, this.getSelections())); + 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 _externalExecuteCommand(command: editorCommon.ICommand): void { - this._cursors.killSecondaryCursors(); + 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 - })); + this._executeEditOperation(new EditOperationResult(EditOperationType.Other, [command], { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: false + })); + }, eventsCollector, source); } - private _externalExecuteCommands(commands: editorCommon.ICommand[]): void { - this._executeEditOperation(new EditOperationResult(EditOperationType.Other, commands, { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: false - })); + 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) => { - 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,20 +875,21 @@ class CommandExecutor { major: majorIdentifier, minor: operationMinor++ }, - range: selection, + range: range, text: text, - forceMoveMarkers: false, + forceMoveMarkers: forceMoveMarkers, isAutoWhitespaceEdit: command.insertsAutoWhitespace }); }; let hadTrackedEditOperation = false; - const addTrackedEditOperation = (selection: Range, text: string | null) => { + const addTrackedEditOperation = (selection: IRange, text: string | null, forceMoveMarkers?: boolean) => { hadTrackedEditOperation = true; - addEditOperation(selection, text); + 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 63cc38e4862e0..746039533adf4 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 009744c4ee796..e49c7dea933ff 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -6,19 +6,17 @@ 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 } from 'vs/editor/common/config/editorOptions'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; +import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingOvertypeStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; 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; } @@ -103,7 +82,7 @@ export class CursorConfiguration { public readonly autoClosingQuotes: EditorAutoClosingStrategy; public readonly autoClosingOvertype: EditorAutoClosingOvertypeStrategy; public readonly autoSurround: EditorAutoSurroundStrategy; - public readonly autoIndent: boolean; + public readonly autoIndent: EditorAutoIndentStrategy; public readonly autoClosingPairsOpen2: Map; public readonly autoClosingPairsClose2: Map; public readonly surroundingPairs: CharacterMap; @@ -357,62 +336,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; } } @@ -523,12 +453,15 @@ export class CursorColumns { if (codePoint === CharCode.Tab) { result = CursorColumns.nextRenderTabStop(result, tabSize); } else { + let graphemeBreakType = strings.getGraphemeBreakType(codePoint); while (i < endOffset) { const nextCodePoint = strings.getNextCodePoint(lineContent, endOffset, i); - if (!strings.isUnicodeMark(nextCodePoint)) { + const nextGraphemeBreakType = strings.getGraphemeBreakType(nextCodePoint); + if (strings.breakBetweenGraphemeBreakType(graphemeBreakType, nextGraphemeBreakType)) { break; } i += (nextCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + graphemeBreakType = nextGraphemeBreakType; } if (strings.isFullWidthCharacter(codePoint) || strings.isEmojiImprecise(codePoint)) { result = result + 2; @@ -582,12 +515,15 @@ export class CursorColumns { if (codePoint === CharCode.Tab) { afterVisibleColumn = CursorColumns.nextRenderTabStop(beforeVisibleColumn, tabSize); } else { + let graphemeBreakType = strings.getGraphemeBreakType(codePoint); while (i < lineLength) { const nextCodePoint = strings.getNextCodePoint(lineContent, lineLength, i); - if (!strings.isUnicodeMark(nextCodePoint)) { + const nextGraphemeBreakType = strings.getGraphemeBreakType(nextCodePoint); + if (strings.breakBetweenGraphemeBreakType(graphemeBreakType, nextGraphemeBreakType)) { break; } i += (nextCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1); + graphemeBreakType = nextGraphemeBreakType; } if (strings.isFullWidthCharacter(codePoint) || strings.isEmojiImprecise(codePoint)) { afterVisibleColumn = beforeVisibleColumn + 2; diff --git a/src/vs/editor/common/controller/cursorMoveCommands.ts b/src/vs/editor/common/controller/cursorMoveCommands.ts index 759ad0e19fd33..bc9bbb1c390bc 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 1d414dd1f32a1..20ca28947b2e0 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 ba0be3d53174d..0155bf76b6e4a 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -19,6 +19,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { EnterAction, IndentAction, StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; +import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; export class TypeOperations { @@ -34,7 +35,8 @@ export class TypeOperations { tabSize: config.tabSize, indentSize: config.indentSize, insertSpaces: config.insertSpaces, - useTabStops: config.useTabStops + useTabStops: config.useTabStops, + autoIndent: config.autoIndent }); } return commands; @@ -48,7 +50,8 @@ export class TypeOperations { tabSize: config.tabSize, indentSize: config.indentSize, insertSpaces: config.insertSpaces, - useTabStops: config.useTabStops + useTabStops: config.useTabStops, + autoIndent: config.autoIndent }); } return commands; @@ -91,7 +94,7 @@ export class TypeOperations { if (pasteOnNewLine) { // Paste entire line at the beginning of line let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, 1); - commands[i] = new ReplaceCommandThatPreservesSelection(typeSelection, text, selection); + commands[i] = new ReplaceCommandThatPreservesSelection(typeSelection, text, selection, true); } else { commands[i] = new ReplaceCommand(selection, text); } @@ -149,15 +152,15 @@ export class TypeOperations { let action: IndentAction | EnterAction | null = null; let indentation: string = ''; - let expectedIndentAction = config.autoIndent ? LanguageConfigurationRegistry.getInheritIndentForLine(model, lineNumber, false) : null; + const expectedIndentAction = LanguageConfigurationRegistry.getInheritIndentForLine(config.autoIndent, model, lineNumber, false); if (expectedIndentAction) { action = expectedIndentAction.action; indentation = expectedIndentAction.indentation; } else if (lineNumber > 1) { let lastLineNumber: number; for (lastLineNumber = lineNumber - 1; lastLineNumber >= 1; lastLineNumber--) { - let lineText = model.getLineContent(lastLineNumber); - let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineText); + const lineText = model.getLineContent(lastLineNumber); + const nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineText); if (nonWhitespaceIdx >= 0) { break; } @@ -168,14 +171,10 @@ export class TypeOperations { return null; } - let maxColumn = model.getLineMaxColumn(lastLineNumber); - let expectedEnterAction = LanguageConfigurationRegistry.getEnterAction(model, new Range(lastLineNumber, maxColumn, lastLineNumber, maxColumn)); + const maxColumn = model.getLineMaxColumn(lastLineNumber); + const expectedEnterAction = LanguageConfigurationRegistry.getEnterAction(config.autoIndent, model, new Range(lastLineNumber, maxColumn, lastLineNumber, maxColumn)); if (expectedEnterAction) { - indentation = expectedEnterAction.indentation; - action = expectedEnterAction.enterAction; - if (action) { - indentation += action.appendText; - } + indentation = expectedEnterAction.indentation + expectedEnterAction.appendText; } } @@ -251,7 +250,8 @@ export class TypeOperations { tabSize: config.tabSize, indentSize: config.indentSize, insertSpaces: config.insertSpaces, - useTabStops: config.useTabStops + useTabStops: config.useTabStops, + autoIndent: config.autoIndent }); } } @@ -269,9 +269,15 @@ export class TypeOperations { 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, { @@ -289,104 +295,97 @@ export class TypeOperations { } private static _enter(config: CursorConfiguration, model: ITextModel, keepPosition: boolean, range: Range): ICommand { - if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { + if (config.autoIndent === EditorAutoIndentStrategy.None) { + return TypeOperations._typeCommand(range, '\n', keepPosition); + } + if (!model.isCheapToTokenize(range.getStartPosition().lineNumber) || config.autoIndent === EditorAutoIndentStrategy.Keep) { let lineText = model.getLineContent(range.startLineNumber); let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); } - let r = LanguageConfigurationRegistry.getEnterAction(model, range); + const r = LanguageConfigurationRegistry.getEnterAction(config.autoIndent, model, range); if (r) { - let enterAction = r.enterAction; - let indentation = r.indentation; - - if (enterAction.indentAction === IndentAction.None) { + if (r.indentAction === IndentAction.None) { // Nothing special - return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation + enterAction.appendText), keepPosition); + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(r.indentation + r.appendText), keepPosition); - } else if (enterAction.indentAction === IndentAction.Indent) { + } else if (r.indentAction === IndentAction.Indent) { // Indent once - return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation + enterAction.appendText), keepPosition); + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(r.indentation + r.appendText), keepPosition); - } else if (enterAction.indentAction === IndentAction.IndentOutdent) { + } else if (r.indentAction === IndentAction.IndentOutdent) { // Ultra special - let normalIndent = config.normalizeIndentation(indentation); - let increasedIndent = config.normalizeIndentation(indentation + enterAction.appendText); + const normalIndent = config.normalizeIndentation(r.indentation); + const increasedIndent = config.normalizeIndentation(r.indentation + r.appendText); - let typeText = '\n' + increasedIndent + '\n' + normalIndent; + const typeText = '\n' + increasedIndent + '\n' + normalIndent; if (keepPosition) { return new ReplaceCommandWithoutChangingPosition(range, typeText, true); } else { return new ReplaceCommandWithOffsetCursorState(range, typeText, -1, increasedIndent.length - normalIndent.length, true); } - } else if (enterAction.indentAction === IndentAction.Outdent) { - let actualIndentation = TypeOperations.unshiftIndent(config, indentation); - return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(actualIndentation + enterAction.appendText), keepPosition); + } else if (r.indentAction === IndentAction.Outdent) { + const actualIndentation = TypeOperations.unshiftIndent(config, r.indentation); + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(actualIndentation + r.appendText), keepPosition); } } - // no enter rules applied, we should check indentation rules then. - if (!config.autoIndent) { - // Nothing special - let lineText = model.getLineContent(range.startLineNumber); - let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); - return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); - } - - let ir = LanguageConfigurationRegistry.getIndentForEnter(model, range, { - unshiftIndent: (indent) => { - return TypeOperations.unshiftIndent(config, indent); - }, - shiftIndent: (indent) => { - return TypeOperations.shiftIndent(config, indent); - }, - normalizeIndentation: (indent) => { - return config.normalizeIndentation(indent); - } - }, config.autoIndent); + const lineText = model.getLineContent(range.startLineNumber); + const indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); - let lineText = model.getLineContent(range.startLineNumber); - let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1); + if (config.autoIndent >= EditorAutoIndentStrategy.Full) { + const ir = LanguageConfigurationRegistry.getIndentForEnter(config.autoIndent, model, range, { + unshiftIndent: (indent) => { + return TypeOperations.unshiftIndent(config, indent); + }, + shiftIndent: (indent) => { + return TypeOperations.shiftIndent(config, indent); + }, + normalizeIndentation: (indent) => { + return config.normalizeIndentation(indent); + } + }); - if (ir) { - let oldEndViewColumn = CursorColumns.visibleColumnFromColumn2(config, model, range.getEndPosition()); - let oldEndColumn = range.endColumn; + if (ir) { + let oldEndViewColumn = CursorColumns.visibleColumnFromColumn2(config, model, range.getEndPosition()); + const oldEndColumn = range.endColumn; - let beforeText = '\n'; - if (indentation !== config.normalizeIndentation(ir.beforeEnter)) { - beforeText = config.normalizeIndentation(ir.beforeEnter) + lineText.substring(indentation.length, range.startColumn - 1) + '\n'; - range = new Range(range.startLineNumber, 1, range.endLineNumber, range.endColumn); - } + let beforeText = '\n'; + if (indentation !== config.normalizeIndentation(ir.beforeEnter)) { + beforeText = config.normalizeIndentation(ir.beforeEnter) + lineText.substring(indentation.length, range.startColumn - 1) + '\n'; + range = new Range(range.startLineNumber, 1, range.endLineNumber, range.endColumn); + } - let newLineContent = model.getLineContent(range.endLineNumber); - let firstNonWhitespace = strings.firstNonWhitespaceIndex(newLineContent); - if (firstNonWhitespace >= 0) { - range = range.setEndPosition(range.endLineNumber, Math.max(range.endColumn, firstNonWhitespace + 1)); - } else { - range = range.setEndPosition(range.endLineNumber, model.getLineMaxColumn(range.endLineNumber)); - } + const newLineContent = model.getLineContent(range.endLineNumber); + const firstNonWhitespace = strings.firstNonWhitespaceIndex(newLineContent); + if (firstNonWhitespace >= 0) { + range = range.setEndPosition(range.endLineNumber, Math.max(range.endColumn, firstNonWhitespace + 1)); + } else { + range = range.setEndPosition(range.endLineNumber, model.getLineMaxColumn(range.endLineNumber)); + } - if (keepPosition) { - return new ReplaceCommandWithoutChangingPosition(range, beforeText + config.normalizeIndentation(ir.afterEnter), true); - } else { - let offset = 0; - if (oldEndColumn <= firstNonWhitespace + 1) { - if (!config.insertSpaces) { - oldEndViewColumn = Math.ceil(oldEndViewColumn / config.indentSize); + if (keepPosition) { + return new ReplaceCommandWithoutChangingPosition(range, beforeText + config.normalizeIndentation(ir.afterEnter), true); + } else { + let offset = 0; + if (oldEndColumn <= firstNonWhitespace + 1) { + if (!config.insertSpaces) { + oldEndViewColumn = Math.ceil(oldEndViewColumn / config.indentSize); + } + offset = Math.min(oldEndViewColumn + 1 - config.normalizeIndentation(ir.afterEnter).length - 1, 0); } - offset = Math.min(oldEndViewColumn + 1 - config.normalizeIndentation(ir.afterEnter).length - 1, 0); + return new ReplaceCommandWithOffsetCursorState(range, beforeText + config.normalizeIndentation(ir.afterEnter), 0, offset, true); } - return new ReplaceCommandWithOffsetCursorState(range, beforeText + config.normalizeIndentation(ir.afterEnter), 0, offset, true); } - - } else { - return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); } + + return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition); } private static _isAutoIndentType(config: CursorConfiguration, model: ITextModel, selections: Selection[]): boolean { - if (!config.autoIndent) { + if (config.autoIndent < EditorAutoIndentStrategy.Full) { return false; } @@ -400,8 +399,8 @@ export class TypeOperations { } private static _runAutoIndentType(config: CursorConfiguration, model: ITextModel, range: Range, ch: string): ICommand | null { - let currentIndentation = LanguageConfigurationRegistry.getIndentationAtPosition(model, range.startLineNumber, range.startColumn); - let actualIndentation = LanguageConfigurationRegistry.getIndentActionForType(model, range, ch, { + const currentIndentation = LanguageConfigurationRegistry.getIndentationAtPosition(model, range.startLineNumber, range.startColumn); + const actualIndentation = LanguageConfigurationRegistry.getIndentActionForType(config.autoIndent, model, range, ch, { shiftIndent: (indentation) => { return TypeOperations.shiftIndent(config, indentation); }, @@ -415,7 +414,7 @@ export class TypeOperations { } if (actualIndentation !== config.normalizeIndentation(currentIndentation)) { - let firstNonWhitespace = model.getLineFirstNonWhitespaceColumn(range.startLineNumber); + const firstNonWhitespace = model.getLineFirstNonWhitespaceColumn(range.startLineNumber); if (firstNonWhitespace === 0) { return TypeOperations._typeCommand( new Range(range.startLineNumber, 0, range.endLineNumber, range.endColumn), @@ -459,9 +458,10 @@ export class TypeOperations { return false; } - // Do not over-type after a backslash + // Do not over-type quotes after a backslash + const chIsQuote = isQuote(ch); const beforeCharacter = position.column > 2 ? lineText.charCodeAt(position.column - 2) : CharCode.Null; - if (beforeCharacter === CharCode.Backslash) { + if (beforeCharacter === CharCode.Backslash && chIsQuote) { return false; } @@ -498,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; } @@ -797,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]); @@ -810,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++) { @@ -828,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)) { @@ -843,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 300174e613aa8..0ab6c93132691 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -10,6 +10,7 @@ import { WordCharacterClass, WordCharacterClassifier, getMapForWordSeparators } 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'; interface IFindWordResult { /** @@ -162,11 +163,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 +174,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 +225,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); } @@ -289,17 +277,26 @@ export class WordOperations { column = model.getLineMaxColumn(lineNumber); } } else if (wordNavigationType === WordNavigationType.WordAccessibility) { + if (movedDown) { + // If we move to the next line, pretend that the cursor is right before the first character. + // This is needed when the first word starts right at the first character - and in order not to miss it, + // we need to start before. + column = 0; + } while ( nextWordOnLine - && nextWordOnLine.wordType === WordType.Separator + && (nextWordOnLine.wordType === WordType.Separator + || nextWordOnLine.start + 1 <= column + ) ) { // Skip over a word made up of one single separator + // Also skip over word if it begins before current cursor position to ascertain we're moving forward at least 1 character. nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1)); } if (nextWordOnLine) { - column = nextWordOnLine.end + 1; + column = nextWordOnLine.start + 1; } else { column = model.getLineMaxColumn(lineNumber); } @@ -330,7 +327,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); } @@ -526,6 +523,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); diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 2bb0958313395..f79fafe21aed3 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/characterClassifier.ts b/src/vs/editor/common/core/characterClassifier.ts index 214bb2abf0533..e5aac27c816bb 100644 --- a/src/vs/editor/common/core/characterClassifier.ts +++ b/src/vs/editor/common/core/characterClassifier.ts @@ -12,14 +12,14 @@ export class CharacterClassifier { /** * Maintain a compact (fully initialized ASCII map for quickly classifying ASCII characters - used more often in code). */ - private _asciiMap: Uint8Array; + protected _asciiMap: Uint8Array; /** * The entire map (sparse array). */ - private _map: Map; + protected _map: Map; - private _defaultValue: number; + protected _defaultValue: number; constructor(_defaultValue: T) { let defaultValue = toUint8(_defaultValue); diff --git a/src/vs/editor/common/core/lineTokens.ts b/src/vs/editor/common/core/lineTokens.ts index ffc1f05f49e92..51aa6a93ca83f 100644 --- a/src/vs/editor/common/core/lineTokens.ts +++ b/src/vs/editor/common/core/lineTokens.ts @@ -67,6 +67,11 @@ export class LineTokens implements IViewLineTokens { return 0; } + public getMetadata(tokenIndex: number): number { + const metadata = this._tokens[(tokenIndex << 1) + 1]; + return metadata; + } + public getLanguageId(tokenIndex: number): LanguageId { const metadata = this._tokens[(tokenIndex << 1) + 1]; return TokenMetadata.getLanguageId(metadata); @@ -132,8 +137,8 @@ export class LineTokens implements IViewLineTokens { while (low < high) { - let mid = low + Math.floor((high - low) / 2); - let endOffset = tokens[(mid << 1)]; + const mid = low + Math.floor((high - low) / 2); + const endOffset = tokens[(mid << 1)]; if (endOffset === desiredIndex) { return mid + 1; diff --git a/src/vs/editor/common/core/range.ts b/src/vs/editor/common/core/range.ts index e212e757dce02..d684e3b952008 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 3a19425bc4d4c..3cfbf4e0488ba 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 36cbe87f54e7d..8a577ab8de24c 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 var 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; } +let _platformTextDecoder: TextDecoder | null; +export function getPlatformTextDecoder(): TextDecoder { + if (!_platformTextDecoder) { + _platformTextDecoder = new TextDecoder(platform.isLittleEndian() ? 'UTF-16LE' : 'UTF-16BE'); + } + return _platformTextDecoder; +} + +export const hasTextDecoder = (typeof TextDecoder !== 'undefined'); export let createStringBuilder: (capacity: number) => IStringBuilder; +export let decodeUTF16LE: (source: Uint8Array, offset: number, len: number) => string; -if (typeof TextDecoder !== 'undefined') { +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 7c4ee1163566d..055a67fbd1511 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 679cb6760233f..94c4a3c48df79 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): 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): 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; @@ -174,6 +177,14 @@ export interface IScrollEvent { readonly scrollHeightChanged: boolean; } +export interface IContentSizeChangedEvent { + readonly contentWidth: number; + readonly contentHeight: number; + + readonly contentWidthChanged: boolean; + readonly contentHeightChanged: boolean; +} + export interface INewScrollPosition { scrollLeft?: number; scrollTop?: number; @@ -347,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. */ @@ -362,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. */ @@ -414,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. */ @@ -434,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. @@ -490,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. */ @@ -630,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 6d401cb5805d4..487e6d4f0fddd 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 25289e8e7f2ee..d15e79eab7d4a 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'; @@ -14,7 +15,8 @@ import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChange 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 } from 'vs/editor/common/model/tokensStore'; +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 { @@ -386,7 +413,7 @@ export class TextModelResolvedOptions { defaultEOL: DefaultEndOfLine; trimAutoWhitespace: boolean; }) { - this.tabSize = src.tabSize | 0; + this.tabSize = Math.max(1, src.tabSize | 0); this.indentSize = src.tabSize | 0; this.insertSpaces = Boolean(src.insertSpaces); this.defaultEOL = src.defaultEOL | 0; @@ -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. @@ -792,6 +837,21 @@ export interface ITextModel { */ setTokens(tokens: MultilineTokens[]): void; + /** + * @internal + */ + setSemanticTokens(tokens: MultilineTokens2[] | null, isComplete: boolean): void; + + /** + * @internal + */ + setPartialSemanticTokens(range: Range, tokens: MultilineTokens2[] | null): void; + + /** + * @internal + */ + hasSemanticTokens(): boolean; + /** * Flush all tokenization state. * @internal @@ -893,7 +953,7 @@ export interface ITextModel { * @param position The position at which to start the search. * @internal */ - findEnclosingBrackets(position: IPosition): [Range, Range] | null; + findEnclosingBrackets(position: IPosition, maxDuration?: number): [Range, Range] | null; /** * Given a `position`, if the position is on top or near a bracket, @@ -1043,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 @@ -1055,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. @@ -1065,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; /** * Is there anything in the undo stack? @@ -1083,7 +1155,7 @@ export interface ITextModel { * The inverse edit operations will be pushed on the undo stack. * @internal */ - redo(): Selection[] | null; + redo(): void; /** * Is there anything in the redo stack? @@ -1203,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; @@ -1223,13 +1312,19 @@ 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; + findMatchesLineByLine(searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[]; +} +/** + * @internal + */ +export interface ITextBuffer extends IReadonlyTextBuffer { setEOL(newEOL: '\r\n' | '\n'): void; - applyEdits(rawOperations: IIdentifiedSingleEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult; - findMatchesLineByLine(searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[]; + applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult; } /** @@ -1238,7 +1333,7 @@ export interface ITextBuffer { 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 add6b310ea56c..5316fce9e95fe 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -3,241 +3,421 @@ * 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'; - -interface IEditOperation { - operations: IIdentifiedSingleEditOperation[]; +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'; + +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 { + + 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, + [] + ); + } - undo(model: TextModel): void; - redo(model: TextModel): void; -} + 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; + } -class EditStackElement implements IStackElement { - public readonly beforeVersionId: number; - public readonly beforeCursorState: Selection[]; - public afterCursorState: Selection[] | null; - public afterVersionId: number; + private static _writeSelectionsSize(selections: Selection[] | null): number { + return 4 + 4 * 4 * (selections ? selections.length : 0); + } - public editOperations: IEditOperation[]; + 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; + } - constructor(beforeVersionId: number, beforeCursorState: Selection[]) { - this.beforeVersionId = beforeVersionId; - this.beforeCursorState = beforeCursorState; - this.afterCursorState = null; - this.afterVersionId = -1; - this.editOperations = []; + 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 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) - }; + 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 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 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 + ); } } -function getModelEOL(model: TextModel): EndOfLineSequence { - const eol = model.getEOL(); - if (eol === '\n') { - return EndOfLineSequence.LF; - } else { - return EndOfLineSequence.CRLF; - } +export interface IUndoRedoDelegate { + prepareUndoRedo(element: MultiModelEditStackElement): Promise | IDisposable | void; } -class EOLStackElement implements IStackElement { - public readonly beforeVersionId: number; - public readonly beforeCursorState: Selection[] | null; - public readonly afterCursorState: Selection[] | null; - public afterVersionId: number; +export class SingleModelEditStackElement implements IResourceUndoRedoElement { - public eol: EndOfLineSequence; + public model: ITextModel | URI; + private _data: SingleModelEditStackData | ArrayBuffer; - constructor(beforeVersionId: number, setEOL: EndOfLineSequence) { - this.beforeVersionId = beforeVersionId; - this.beforeCursorState = null; - this.afterCursorState = null; - this.afterVersionId = -1; - this.eol = setEOL; + public get type(): UndoRedoElementType.Resource { + return UndoRedoElementType.Resource; } - public undo(model: TextModel): void { - let redoEOL = getModelEOL(model); - model.setEOL(this.eol); - this.eol = redoEOL; + public get resource(): URI { + if (URI.isUri(this.model)) { + return this.model; + } + return this.model.uri; } - public redo(model: TextModel): void { - let undoEOL = getModelEOL(model); - model.setEOL(this.eol); - this.eol = undoEOL; + public get label(): string { + return nls.localize('edit', "Typing"); } -} -export interface IUndoRedoResult { - selections: Selection[] | null; - recordedVersionId: number; -} + constructor(model: ITextModel, beforeCursorState: Selection[] | null) { + this.model = model; + this._data = SingleModelEditStackData.create(model, beforeCursorState); + } -export class EditStack { + public toString(): string { + const data = (this._data instanceof SingleModelEditStackData ? this._data : SingleModelEditStackData.deserialize(this._data)); + return data.changes.map(change => change.toString()).join(', '); + } - private readonly model: TextModel; - private currentOpenStackElement: IStackElement | null; - private past: IStackElement[]; - private future: IStackElement[]; + public matchesResource(resource: URI): boolean { + const uri = (URI.isUri(this.model) ? this.model : this.model.uri); + return (uri.toString() === resource.toString()); + } - constructor(model: TextModel) { + public setModel(model: ITextModel | URI): void { this.model = model; - this.currentOpenStackElement = null; - this.past = []; - this.future = []; } - public pushStackElement(): void { - if (this.currentOpenStackElement !== null) { - this.past.push(this.currentOpenStackElement); - this.currentOpenStackElement = null; + 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 clear(): void { - this.currentOpenStackElement = null; - this.past = []; - this.future = []; + public close(): void { + if (this._data instanceof SingleModelEditStackData) { + this._data = this._data.serialize(); + } } - public pushEOL(eol: EndOfLineSequence): void { - // No support for parallel universes :( - this.future = []; + 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); + } - if (this.currentOpenStackElement) { - this.pushStackElement(); + public heapSize(): number { + if (this._data instanceof SingleModelEditStackData) { + this._data = this._data.serialize(); } + return this._data.byteLength + 168/*heap overhead*/; + } +} + +export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement { + + public readonly type = UndoRedoElementType.Workspace; + public readonly label: string; + private _isOpen: boolean; - const prevEOL = getModelEOL(this.model); - let stackElement = new EOLStackElement(this.model.getAlternativeVersionId(), prevEOL); + private readonly _editStackElementsArr: SingleModelEditStackElement[]; + private readonly _editStackElementsMap: Map; - this.model.setEOL(eol); + private _delegate: IUndoRedoDelegate | null; - stackElement.afterVersionId = this.model.getVersionId(); - this.currentOpenStackElement = stackElement; - this.pushStackElement(); + public get resources(): readonly URI[] { + return this._editStackElementsArr.map(editStackElement => editStackElement.resource); } - public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer | null): Selection[] | null { - // No support for parallel universes :( - this.future = []; + 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; + } - let stackElement: EditStackElement | null = null; + public setDelegate(delegate: IUndoRedoDelegate): void { + this._delegate = delegate; + } - if (this.currentOpenStackElement) { - if (this.currentOpenStackElement instanceof EditStackElement) { - stackElement = this.currentOpenStackElement; - } else { - this.pushStackElement(); - } + public prepareUndoRedo(): Promise | IDisposable | void { + if (this._delegate) { + return this._delegate.prepareUndoRedo(this); } + } - if (!this.currentOpenStackElement) { - stackElement = new EditStackElement(this.model.getAlternativeVersionId(), beforeCursorState); - this.currentOpenStackElement = stackElement; + public getMissingModels(): URI[] { + const result: URI[] = []; + for (const editStackElement of this._editStackElementsArr) { + if (URI.isUri(editStackElement.model)) { + result.push(editStackElement.model); + } } + return result; + } - const inverseEditOperation: IEditOperation = { - operations: this.model.applyEdits(editOperations) - }; + public matchesResource(resource: URI): boolean { + const key = uriGetComparisonKey(resource); + return (this._editStackElementsMap.has(key)); + } - stackElement!.editOperations.push(inverseEditOperation); - stackElement!.afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperation.operations); - stackElement!.afterVersionId = this.model.getVersionId(); - return stackElement!.afterCursorState; + 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); + } } - private static _computeCursorState(cursorStateComputer: ICursorStateComputer | null, inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] | null { - try { - return cursorStateComputer ? cursorStateComputer(inverseEditOperations) : null; - } catch (e) { - onUnexpectedError(e); - return null; + 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 undo(): IUndoRedoResult | null { + 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); + } - this.pushStackElement(); + public close(): void { + this._isOpen = false; + } - if (this.past.length > 0) { - const pastStackElement = this.past.pop()!; + public undo(): void { + this._isOpen = false; - try { - pastStackElement.undo(this.model); - } catch (e) { - onUnexpectedError(e); - this.clear(); - return null; - } + for (const editStackElement of this._editStackElementsArr) { + editStackElement.undo(); + } + } - this.future.push(pastStackElement); + public redo(): void { + for (const editStackElement of this._editStackElementsArr) { + editStackElement.redo(); + } + } - return { - selections: pastStackElement.beforeCursorState, - recordedVersionId: pastStackElement.beforeVersionId - }; + 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; + } +} + +export type EditStackElement = SingleModelEditStackElement | MultiModelEditStackElement; - return null; +function getModelEOL(model: ITextModel): EndOfLineSequence { + const eol = model.getEOL(); + if (eol === '\n') { + return EndOfLineSequence.LF; + } else { + return EndOfLineSequence.CRLF; } +} - public canUndo(): boolean { - return (this.past.length > 0) || this.currentOpenStackElement !== null; +export function isEditStackElement(element: IResourceUndoRedoElement | IWorkspaceUndoRedoElement | null): element is EditStackElement { + if (!element) { + return false; } + return ((element instanceof SingleModelEditStackElement) || (element instanceof MultiModelEditStackElement)); +} - public redo(): IUndoRedoResult | null { +export class EditStack { - if (this.future.length > 0) { - const futureStackElement = this.future.pop()!; + private readonly _model: TextModel; + private readonly _undoRedoService: IUndoRedoService; - try { - futureStackElement.redo(this.model); - } catch (e) { - onUnexpectedError(e); - this.clear(); - return null; - } + constructor(model: TextModel, undoRedoService: IUndoRedoService) { + this._model = model; + this._undoRedoService = undoRedoService; + } + + public pushStackElement(): void { + const lastElement = this._undoRedoService.getLastElement(this._model.uri); + if (isEditStackElement(lastElement)) { + lastElement.close(); + } + } - this.past.push(futureStackElement); + public clear(): void { + this._undoRedoService.removeElements(this._model.uri); + } - return { - selections: futureStackElement.afterCursorState, - recordedVersionId: futureStackElement.afterVersionId - }; + 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 { + const editStackElement = this._getOrCreateEditStackElement(null); + this._model.setEOL(eol); + editStackElement.append(this._model, [], getModelEOL(this._model), this._model.getAlternativeVersionId(), null); + } - return null; + 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; + } + 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; } - public canRedo(): boolean { - return (this.future.length > 0); + private static _computeCursorState(cursorStateComputer: ICursorStateComputer | null, inverseEditOperations: IValidEditOperation[]): Selection[] | null { + try { + return cursorStateComputer ? cursorStateComputer(inverseEditOperations) : null; + } catch (e) { + onUnexpectedError(e); + return null; + } } } diff --git a/src/vs/editor/common/model/indentationGuesser.ts b/src/vs/editor/common/model/indentationGuesser.ts index b1790f97bdbdc..396f4cbb9559f 100644 --- a/src/vs/editor/common/model/indentationGuesser.ts +++ b/src/vs/editor/common/model/indentationGuesser.ts @@ -72,11 +72,12 @@ function spacesDiff(a: string, aLength: number, b: string, bLength: number, resu if (spacesDiff > 0 && 0 <= bSpacesCnt - 1 && bSpacesCnt - 1 < a.length && bSpacesCnt < b.length) { if (b.charCodeAt(bSpacesCnt) !== CharCode.Space && a.charCodeAt(bSpacesCnt - 1) === CharCode.Space) { - // This looks like an alignment desire: e.g. - // const a = b + c, - // d = b - c; - - result.looksLikeAlignment = true; + if (a.charCodeAt(a.length - 1) === CharCode.Comma) { + // This looks like an alignment desire: e.g. + // const a = b + c, + // d = b - c; + result.looksLikeAlignment = true; + } } } return; diff --git a/src/vs/editor/common/model/mirrorTextModel.ts b/src/vs/editor/common/model/mirrorTextModel.ts index 25b3d150ce078..6a1272552b0e1 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 1c844dd66c4bb..03a518eb523e8 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; } @@ -511,7 +511,93 @@ export class PieceTreeBase { } public getLinesContent(): string[] { - return this.getContentOfSubTree(this.root).split(/\r\n|\r|\n/); + let lines: string[] = []; + let linesLength = 0; + let currentLine = ''; + let danglingCR = false; + + this.iterate(this.root, node => { + if (node === SENTINEL) { + return true; + } + + const piece = node.piece; + let pieceLength = piece.length; + if (pieceLength === 0) { + return true; + } + + const buffer = this._buffers[piece.bufferIndex].buffer; + const lineStarts = this._buffers[piece.bufferIndex].lineStarts; + + const pieceStartLine = piece.start.line; + const pieceEndLine = piece.end.line; + let pieceStartOffset = lineStarts[pieceStartLine] + piece.start.column; + + if (danglingCR) { + if (buffer.charCodeAt(pieceStartOffset) === CharCode.LineFeed) { + // pretend the \n was in the previous piece.. + pieceStartOffset++; + pieceLength--; + } + lines[linesLength++] = currentLine; + currentLine = ''; + danglingCR = false; + if (pieceLength === 0) { + return true; + } + } + + if (pieceStartLine === pieceEndLine) { + // this piece has no new lines + if (!this._EOLNormalized && buffer.charCodeAt(pieceStartOffset + pieceLength - 1) === CharCode.CarriageReturn) { + danglingCR = true; + currentLine += buffer.substr(pieceStartOffset, pieceLength - 1); + } else { + currentLine += buffer.substr(pieceStartOffset, pieceLength); + } + return true; + } + + // add the text before the first line start in this piece + currentLine += ( + this._EOLNormalized + ? buffer.substring(pieceStartOffset, Math.max(pieceStartOffset, lineStarts[pieceStartLine + 1] - this._EOLLength)) + : buffer.substring(pieceStartOffset, lineStarts[pieceStartLine + 1]).replace(/(\r\n|\r|\n)$/, '') + ); + lines[linesLength++] = currentLine; + + for (let line = pieceStartLine + 1; line < pieceEndLine; line++) { + currentLine = ( + this._EOLNormalized + ? buffer.substring(lineStarts[line], lineStarts[line + 1] - this._EOLLength) + : buffer.substring(lineStarts[line], lineStarts[line + 1]).replace(/(\r\n|\r|\n)$/, '') + ); + lines[linesLength++] = currentLine; + } + + if (!this._EOLNormalized && buffer.charCodeAt(lineStarts[pieceEndLine] + piece.end.column - 1) === CharCode.CarriageReturn) { + danglingCR = true; + if (piece.end.column === 0) { + // The last line ended with a \r, let's undo the push, it will be pushed by next iteration + linesLength--; + } else { + currentLine = buffer.substr(lineStarts[pieceEndLine], piece.end.column - 1); + } + } else { + currentLine = buffer.substr(lineStarts[pieceEndLine], piece.end.column); + } + + return true; + }); + + if (danglingCR) { + lines[linesLength++] = currentLine; + currentLine = ''; + } + + lines[linesLength++] = currentLine; + return lines; } public getLength(): number { @@ -540,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(); @@ -561,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); @@ -569,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); @@ -584,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; @@ -728,7 +823,7 @@ export class PieceTreeBase { // #endregion // #region Piece Table - insert(offset: number, value: string, eolNormalized: boolean = false): void { + public insert(offset: number, value: string, eolNormalized: boolean = false): void { this._EOLNormalized = this._EOLNormalized && eolNormalized; this._lastVisitedLine.lineNumber = 0; this._lastVisitedLine.value = ''; @@ -752,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[] = []; @@ -826,7 +921,7 @@ export class PieceTreeBase { this.computeBufferMetadata(); } - delete(offset: number, cnt: number): void { + public delete(offset: number, cnt: number): void { this._lastVisitedLine.lineNumber = 0; this._lastVisitedLine.value = ''; @@ -852,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; @@ -875,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); } @@ -899,7 +994,7 @@ export class PieceTreeBase { this.computeBufferMetadata(); } - insertContentToNodeLeft(value: string, node: TreeNode) { + private insertContentToNodeLeft(value: string, node: TreeNode) { // we are inserting content to the beginning of node let nodesToDel: TreeNode[] = []; if (this.shouldCheckCRLF() && this.endWithCR(value) && this.startWithLF(node)) { @@ -934,7 +1029,7 @@ export class PieceTreeBase { this.deleteNodes(nodesToDel); } - insertContentToNodeRight(value: string, node: TreeNode) { + private insertContentToNodeRight(value: string, node: TreeNode) { // we are inserting to the right of this node. if (this.adjustCarriageReturnFromNext(value, node)) { // move \n to the new node. @@ -952,9 +1047,9 @@ export class PieceTreeBase { this.validateCRLFWithPrevNode(newNode); } - positionInBuffer(node: TreeNode, remainder: number): BufferCursor; - positionInBuffer(node: TreeNode, remainder: number, ret: BufferCursor): null; - positionInBuffer(node: TreeNode, remainder: number, ret?: BufferCursor): BufferCursor | null { + private positionInBuffer(node: TreeNode, remainder: number): BufferCursor; + private positionInBuffer(node: TreeNode, remainder: number, ret: BufferCursor): null; + private positionInBuffer(node: TreeNode, remainder: number, ret?: BufferCursor): BufferCursor | null { let piece = node.piece; let bufferIndex = node.piece.bufferIndex; let lineStarts = this._buffers[bufferIndex].lineStarts; @@ -1002,7 +1097,7 @@ export class PieceTreeBase { }; } - getLineFeedCnt(bufferIndex: number, start: BufferCursor, end: BufferCursor): number { + private getLineFeedCnt(bufferIndex: number, start: BufferCursor, end: BufferCursor): number { // we don't need to worry about start: abc\r|\n, or abc|\r, or abc|\n, or abc|\r\n doesn't change the fact that, there is one line break after start. // now let's take care of end: abc\r|\n, if end is in between \r and \n, we need to add line feed count by 1 if (end.column === 0) { @@ -1032,18 +1127,18 @@ export class PieceTreeBase { } } - offsetInBuffer(bufferIndex: number, cursor: BufferCursor): number { + private offsetInBuffer(bufferIndex: number, cursor: BufferCursor): number { let lineStarts = this._buffers[bufferIndex].lineStarts; return lineStarts[cursor.line] + cursor.column; } - deleteNodes(nodes: TreeNode[]): void { + private deleteNodes(nodes: TreeNode[]): void { for (let i = 0; i < nodes.length; i++) { rbDelete(this, nodes[i]); } } - createNewPieces(text: string): Piece[] { + private createNewPieces(text: string): Piece[] { if (text.length > AverageBufferSize) { // the content is large, operations like substring, charCode becomes slow // so here we split it into smaller chunks, just like what we did for CR/LF normalization @@ -1128,25 +1223,25 @@ export class PieceTreeBase { return [newPiece]; } - getLinesRawContent(): string { + public getLinesRawContent(): string { return this.getContentOfSubTree(this.root); } - getLineRawContent(lineNumber: number, endOffset: number = 0): string { + public getLineRawContent(lineNumber: number, endOffset: number = 0): string { let x = this.root; let ret = ''; 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; @@ -1155,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; @@ -1166,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; @@ -1188,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); @@ -1204,7 +1299,7 @@ export class PieceTreeBase { return ret; } - computeBufferMetadata() { + private computeBufferMetadata() { let x = this.root; let lfCnt = 1; @@ -1218,11 +1313,11 @@ export class PieceTreeBase { this._lineCnt = lfCnt; this._length = len; - this._searchCache.valdiate(this._length); + this._searchCache.validate(this._length); } // #region node operations - getIndexOf(node: TreeNode, accumulatedValue: number): { index: number, remainder: number } { + private getIndexOf(node: TreeNode, accumulatedValue: number): { index: number, remainder: number } { let piece = node.piece; let pos = this.positionInBuffer(node, accumulatedValue); let lineCnt = pos.line - piece.start.line; @@ -1239,7 +1334,7 @@ export class PieceTreeBase { return { index: lineCnt, remainder: pos.column }; } - getAccumulatedValue(node: TreeNode, index: number) { + private getAccumulatedValue(node: TreeNode, index: number) { if (index < 0) { return 0; } @@ -1253,7 +1348,7 @@ export class PieceTreeBase { } } - deleteNodeTail(node: TreeNode, pos: BufferCursor) { + private deleteNodeTail(node: TreeNode, pos: BufferCursor) { const piece = node.piece; const originalLFCnt = piece.lineFeedCnt; const originalEndOffset = this.offsetInBuffer(piece.bufferIndex, piece.end); @@ -1277,7 +1372,7 @@ export class PieceTreeBase { updateTreeMetadata(this, node, size_delta, lf_delta); } - deleteNodeHead(node: TreeNode, pos: BufferCursor) { + private deleteNodeHead(node: TreeNode, pos: BufferCursor) { const piece = node.piece; const originalLFCnt = piece.lineFeedCnt; const originalStartOffset = this.offsetInBuffer(piece.bufferIndex, piece.start); @@ -1299,7 +1394,7 @@ export class PieceTreeBase { updateTreeMetadata(this, node, size_delta, lf_delta); } - shrinkNode(node: TreeNode, start: BufferCursor, end: BufferCursor) { + private shrinkNode(node: TreeNode, start: BufferCursor, end: BufferCursor) { const piece = node.piece; const originalStartPos = piece.start; const originalEndPos = piece.end; @@ -1334,7 +1429,7 @@ export class PieceTreeBase { this.validateCRLFWithPrevNode(newNode); } - appendToNode(node: TreeNode, value: string): void { + private appendToNode(node: TreeNode, value: string): void { if (this.adjustCarriageReturnFromNext(value, node)) { value += '\n'; } @@ -1374,7 +1469,7 @@ export class PieceTreeBase { updateTreeMetadata(this, node, value.length, lf_delta); } - nodeAt(offset: number): NodePosition { + private nodeAt(offset: number): NodePosition { let x = this.root; let cache = this._searchCache.get(offset); if (cache) { @@ -1409,7 +1504,7 @@ export class PieceTreeBase { return null!; } - nodeAt2(lineNumber: number, column: number): NodePosition { + private nodeAt2(lineNumber: number, column: number): NodePosition { let x = this.root; let nodeStartOffset = 0; @@ -1418,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) { @@ -1450,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 { @@ -1476,7 +1571,7 @@ export class PieceTreeBase { return null!; } - nodeCharCodeAt(node: TreeNode, offset: number): number { + private nodeCharCodeAt(node: TreeNode, offset: number): number { if (node.piece.lineFeedCnt < 1) { return -1; } @@ -1485,7 +1580,7 @@ export class PieceTreeBase { return buffer.buffer.charCodeAt(newOffset); } - offsetOfNode(node: TreeNode): number { + private offsetOfNode(node: TreeNode): number { if (!node) { return 0; } @@ -1504,11 +1599,11 @@ export class PieceTreeBase { // #endregion // #region CRLF - shouldCheckCRLF() { + private shouldCheckCRLF() { return !(this._EOLNormalized && this._EOL === '\n'); } - startWithLF(val: string | TreeNode): boolean { + private startWithLF(val: string | TreeNode): boolean { if (typeof val === 'string') { return val.charCodeAt(0) === 10; } @@ -1532,7 +1627,7 @@ export class PieceTreeBase { return this._buffers[piece.bufferIndex].buffer.charCodeAt(startOffset) === 10; } - endWithCR(val: string | TreeNode): boolean { + private endWithCR(val: string | TreeNode): boolean { if (typeof val === 'string') { return val.charCodeAt(val.length - 1) === 13; } @@ -1544,7 +1639,7 @@ export class PieceTreeBase { return this.nodeCharCodeAt(val, val.piece.length - 1) === 13; } - validateCRLFWithPrevNode(nextNode: TreeNode) { + private validateCRLFWithPrevNode(nextNode: TreeNode) { if (this.shouldCheckCRLF() && this.startWithLF(nextNode)) { let node = nextNode.prev(); if (this.endWithCR(node)) { @@ -1553,7 +1648,7 @@ export class PieceTreeBase { } } - validateCRLFWithNextNode(node: TreeNode) { + private validateCRLFWithNextNode(node: TreeNode) { if (this.shouldCheckCRLF() && this.endWithCR(node)) { let nextNode = node.next(); if (this.startWithLF(nextNode)) { @@ -1562,7 +1657,7 @@ export class PieceTreeBase { } } - fixCRLF(prev: TreeNode, next: TreeNode) { + private fixCRLF(prev: TreeNode, next: TreeNode) { let nodesToDel: TreeNode[] = []; // update node let lineStarts = this._buffers[prev.piece.bufferIndex].lineStarts; @@ -1617,7 +1712,7 @@ export class PieceTreeBase { } } - adjustCarriageReturnFromNext(value: string, node: TreeNode): boolean { + private adjustCarriageReturnFromNext(value: string, node: TreeNode): boolean { if (this.shouldCheckCRLF() && this.endWithCR(value)) { let nextNode = node.next(); if (this.startWithLF(nextNode)) { @@ -1667,7 +1762,7 @@ export class PieceTreeBase { return callback(node) && this.iterate(node.right, callback); } - getNodeContent(node: TreeNode) { + private getNodeContent(node: TreeNode) { if (node === SENTINEL) { return ''; } @@ -1695,7 +1790,7 @@ export class PieceTreeBase { * / * z */ - rbInsertRight(node: TreeNode | null, p: Piece): TreeNode { + private rbInsertRight(node: TreeNode | null, p: Piece): TreeNode { let z = new TreeNode(p, NodeColor.Red); z.left = SENTINEL; z.right = SENTINEL; @@ -1727,7 +1822,7 @@ export class PieceTreeBase { * \ * z */ - rbInsertLeft(node: TreeNode | null, p: Piece): TreeNode { + private rbInsertLeft(node: TreeNode | null, p: Piece): TreeNode { let z = new TreeNode(p, NodeColor.Red); z.left = SENTINEL; z.right = SENTINEL; @@ -1751,7 +1846,7 @@ export class PieceTreeBase { return z; } - getContentOfSubTree(node: TreeNode): string { + private getContentOfSubTree(node: TreeNode): string { let str = ''; this.iterate(node, node => { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index a987231c3abee..62ab292991092 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 }[] = []; - - 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 (recordTrimAutoWhitespace) { + for (let i = 0; i < operations.length; i++) { + let op = operations[i]; + let reverseRange = reverseRanges[i]; + + 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) { + + 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) + }; + } - reverseOperations[i] = { - sortIndex: op.sortIndex, - identifier: op.identifier, - range: reverseRange, - text: this.getValueInRange(op.range), - forceMoveMarkers: op.forceMoveMarkers - }; + // 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 fa8d98e94e643..d134517ba1570 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 0000000000000..c441bf7abc91f --- /dev/null +++ b/src/vs/editor/common/model/textChange.ts @@ -0,0 +1,336 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +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} "${this.newText}")`; + } + if (this.newText.length === 0) { + return `(delete@${this.oldPosition} "${this.oldText}")`; + } + return `(replace@${this.oldPosition} "${this.oldText}" with "${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 ff39a2066f64f..cb7b34b345194 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -29,11 +29,14 @@ 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 } from 'vs/editor/common/model/tokensStore'; +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() { return new PieceTreeTextBufferBuilder(); @@ -157,6 +160,32 @@ class TextModelSnapshot implements model.ITextSnapshot { const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; +const enum StringOffsetValidationType { + /** + * Even allowed in surrogate pairs + */ + Relaxed = 0, + /** + * Not allowed in surrogate pairs + */ + 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 @@ -174,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); @@ -240,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; @@ -251,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; @@ -276,10 +303,17 @@ export class TextModel extends Disposable implements model.ITextModel { private _languageIdentifier: LanguageIdentifier; private readonly _languageRegistryListener: IDisposable; private readonly _tokens: TokensStore; + private readonly _tokens2: TokensStore2; 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 @@ -291,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); @@ -316,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; @@ -333,12 +369,13 @@ 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; this._tokens = new TokensStore(); + this._tokens2 = new TokensStore2(); this._tokenization = new TextModelTokenization(this); } @@ -363,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 @@ -414,13 +456,14 @@ export class TextModel extends Disposable implements model.ITextModel { // Flush all tokens this._tokens.flush(); + this._tokens2.flush(); // Destroy all my decorations this._decorations = Object.create(null); this._decorationsTree = new DecorationsTrees(); // Destroy my edit history and settings - this._commandManager = new EditStack(this); + this._commandManager.clear(); this._trimAutoWhitespaceLines = null; this._emitContentChangedEvent( @@ -658,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(); } @@ -667,9 +720,14 @@ 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, false); + let position = this._validatePosition(rawPosition.lineNumber, rawPosition.column, StringOffsetValidationType.Relaxed); return this._buffer.getOffsetAt(position.lineNumber, position.column); } @@ -684,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(); @@ -864,10 +930,7 @@ export class TextModel extends Disposable implements model.ITextModel { return new Range(startLineNumber, startColumn, endLineNumber, endColumn); } - /** - * @param strict Do NOT allow a position inside a high-low surrogate pair - */ - private _isValidPosition(lineNumber: number, column: number, strict: boolean): boolean { + private _isValidPosition(lineNumber: number, column: number, validationType: StringOffsetValidationType): boolean { if (typeof lineNumber !== 'number' || typeof column !== 'number') { return false; } @@ -889,14 +952,19 @@ export class TextModel extends Disposable implements model.ITextModel { return false; } + if (column === 1) { + return true; + } + const maxColumn = this.getLineMaxColumn(lineNumber); if (column > maxColumn) { return false; } - if (strict) { - const [charStartOffset,] = strings.getCharContainingOffset(this._buffer.getLineContent(lineNumber), column - 1); - if (column !== charStartOffset + 1) { + if (validationType === StringOffsetValidationType.SurrogatePairs) { + // !!At this point, column > 1 + const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2); + if (strings.isHighSurrogate(charCodeBefore)) { return false; } } @@ -904,10 +972,7 @@ export class TextModel extends Disposable implements model.ITextModel { return true; } - /** - * @param strict Do NOT allow a position inside a high-low surrogate pair - */ - private _validatePosition(_lineNumber: number, _column: number, strict: boolean): Position { + private _validatePosition(_lineNumber: number, _column: number, validationType: StringOffsetValidationType): Position { const lineNumber = Math.floor((typeof _lineNumber === 'number' && !isNaN(_lineNumber)) ? _lineNumber : 1); const column = Math.floor((typeof _column === 'number' && !isNaN(_column)) ? _column : 1); const lineCount = this._buffer.getLineCount(); @@ -929,10 +994,13 @@ export class TextModel extends Disposable implements model.ITextModel { return new Position(lineNumber, maxColumn); } - if (strict) { - const [charStartOffset,] = strings.getCharContainingOffset(this._buffer.getLineContent(lineNumber), column - 1); - if (column !== charStartOffset + 1) { - return new Position(lineNumber, charStartOffset + 1); + if (validationType === StringOffsetValidationType.SurrogatePairs) { + // If the position would end up in the middle of a high-low surrogate pair, + // we move it to before the pair + // !!At this point, column > 1 + const charCodeBefore = this._buffer.getLineCharCode(lineNumber, column - 2); + if (strings.isHighSurrogate(charCodeBefore)) { + return new Position(lineNumber, column - 1); } } @@ -940,94 +1008,95 @@ export class TextModel extends Disposable implements model.ITextModel { } public validatePosition(position: IPosition): Position { + const validationType = StringOffsetValidationType.SurrogatePairs; this._assertNotDisposed(); // Avoid object allocation and cover most likely case if (position instanceof Position) { - if (this._isValidPosition(position.lineNumber, position.column, true)) { + if (this._isValidPosition(position.lineNumber, position.column, validationType)) { return position; } } - return this._validatePosition(position.lineNumber, position.column, true); + return this._validatePosition(position.lineNumber, position.column, validationType); } - /** - * @param strict Do NOT allow a range to have its boundaries inside a high-low surrogate pair - */ - private _isValidRange(range: Range, strict: boolean): boolean { + private _isValidRange(range: Range, validationType: StringOffsetValidationType): boolean { const startLineNumber = range.startLineNumber; const startColumn = range.startColumn; const endLineNumber = range.endLineNumber; const endColumn = range.endColumn; - if (!this._isValidPosition(startLineNumber, startColumn, false)) { + if (!this._isValidPosition(startLineNumber, startColumn, StringOffsetValidationType.Relaxed)) { return false; } - if (!this._isValidPosition(endLineNumber, endColumn, false)) { + if (!this._isValidPosition(endLineNumber, endColumn, StringOffsetValidationType.Relaxed)) { return false; } - if (strict) { - const startLineContent = this._buffer.getLineContent(startLineNumber); - if (startColumn < startLineContent.length + 1) { - const [charStartOffset,] = strings.getCharContainingOffset(startLineContent, startColumn - 1); - if (startColumn !== charStartOffset + 1) { - return false; - } - } + if (validationType === StringOffsetValidationType.SurrogatePairs) { + const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0); + const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0); - if (endColumn >= 2) { - const endLineContent = (endLineNumber === startLineNumber ? startLineContent : this._buffer.getLineContent(endLineNumber)); - const [, charEndOffset] = strings.getCharContainingOffset(endLineContent, endColumn - 2); - if (endColumn !== charEndOffset + 1) { - return false; - } - } + const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart); + const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd); - return true; + if (!startInsideSurrogatePair && !endInsideSurrogatePair) { + return true; + } + return false; } return true; } public validateRange(_range: IRange): Range { + const validationType = StringOffsetValidationType.SurrogatePairs; this._assertNotDisposed(); // Avoid object allocation and cover most likely case if ((_range instanceof Range) && !(_range instanceof Selection)) { - if (this._isValidRange(_range, true)) { + if (this._isValidRange(_range, validationType)) { return _range; } } - const start = this._validatePosition(_range.startLineNumber, _range.startColumn, false); - const end = this._validatePosition(_range.endLineNumber, _range.endColumn, false); + const start = this._validatePosition(_range.startLineNumber, _range.startColumn, StringOffsetValidationType.Relaxed); + const end = this._validatePosition(_range.endLineNumber, _range.endColumn, StringOffsetValidationType.Relaxed); const startLineNumber = start.lineNumber; - let startColumn = start.column; + const startColumn = start.column; const endLineNumber = end.lineNumber; - let endColumn = end.column; - const isEmpty = (startLineNumber === endLineNumber && startColumn === endColumn); - - const startLineContent = this._buffer.getLineContent(startLineNumber); - if (startColumn < startLineContent.length + 1) { - const [charStartOffset,] = strings.getCharContainingOffset(startLineContent, startColumn - 1); - if (startColumn !== charStartOffset + 1) { - if (isEmpty) { - // do not expand a collapsed range, simply move it to a valid location - return new Range(startLineNumber, charStartOffset + 1, startLineNumber, charStartOffset + 1); - } - startColumn = charStartOffset + 1; + const endColumn = end.column; + + if (validationType === StringOffsetValidationType.SurrogatePairs) { + const charCodeBeforeStart = (startColumn > 1 ? this._buffer.getLineCharCode(startLineNumber, startColumn - 2) : 0); + const charCodeBeforeEnd = (endColumn > 1 && endColumn <= this._buffer.getLineLength(endLineNumber) ? this._buffer.getLineCharCode(endLineNumber, endColumn - 2) : 0); + + const startInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeStart); + const endInsideSurrogatePair = strings.isHighSurrogate(charCodeBeforeEnd); + + if (!startInsideSurrogatePair && !endInsideSurrogatePair) { + return new Range(startLineNumber, startColumn, endLineNumber, endColumn); + } + + if (startLineNumber === endLineNumber && startColumn === endColumn) { + // do not expand a collapsed range, simply move it to a valid location + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn - 1); + } + + if (startInsideSurrogatePair && endInsideSurrogatePair) { + // expand range at both ends + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn + 1); } - } - if (endColumn >= 2) { - const endLineContent = (endLineNumber === startLineNumber ? startLineContent : this._buffer.getLineContent(endLineNumber)); - const [, charEndOffset] = strings.getCharContainingOffset(endLineContent, endColumn - 2); - if (endColumn !== charEndOffset + 1) { - endColumn = charEndOffset + 1; + if (startInsideSurrogatePair) { + // only expand range at the start + return new Range(startLineNumber, startColumn - 1, endLineNumber, endColumn); } + + // only expand range at the end + return new Range(startLineNumber, startColumn, endLineNumber, endColumn + 1); } return new Range(startLineNumber, startColumn, endLineNumber, endColumn); @@ -1049,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); @@ -1068,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 { @@ -1128,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(); @@ -1135,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). @@ -1161,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) { @@ -1219,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)); } } @@ -1230,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._eventEmitter.endDeferredEmit(); + this._isUndoing = false; + this._isRedoing = false; + this._eventEmitter.endDeferredEmit(resultingSelection); 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); + 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 _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; @@ -1262,8 +1425,9 @@ export class TextModel extends Disposable implements model.ITextModel { let lineCount = oldLineCount; for (let i = 0, len = contentChanges.length; i < len; i++) { const change = contentChanges[i]; - const [eolCount, firstLineLength] = countEOL(change.text); + const [eolCount, firstLineLength, lastLineLength] = countEOL(change.text); this._tokens.acceptEdit(change.range, eolCount, firstLineLength); + this._tokens2.acceptEdit(change.range, eolCount, firstLineLength, lastLineLength, change.text.length > 0 ? change.text.charCodeAt(0) : CharCode.Null); this._onDidChangeDecorations.fire(); this._decorationsTree.acceptReplace(change.rangeOffset, change.rangeLength, change.text.length, change.forceMoveMarkers); @@ -1324,65 +1488,23 @@ export class TextModel extends Disposable implements model.ITextModel { ); } - return 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; + return (result.reverseEdits === null ? undefined : result.reverseEdits); } - public undo(): Selection[] | null { - try { - this._onDidChangeDecorations.beginDeferredEmit(); - this._eventEmitter.beginDeferredEmit(); - return this._undo(); - } finally { - this._eventEmitter.endDeferredEmit(); - this._onDidChangeDecorations.endDeferredEmit(); - } + public undo(): void { + this._undoRedoService.undo(this.uri); } public canUndo(): boolean { - return this._commandManager.canUndo(); - } - - 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; + return this._undoRedoService.canUndo(this.uri); } - public redo(): Selection[] | null { - try { - this._onDidChangeDecorations.beginDeferredEmit(); - this._eventEmitter.beginDeferredEmit(); - return this._redo(); - } finally { - this._eventEmitter.endDeferredEmit(); - this._onDidChangeDecorations.endDeferredEmit(); - } + public redo(): void { + this._undoRedoService.redo(this.uri); } public canRedo(): boolean { - return this._commandManager.canRedo(); + return this._undoRedoService.canRedo(this.uri); } //#endregion @@ -1403,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[] => { @@ -1423,7 +1541,6 @@ export class TextModel extends Disposable implements model.ITextModel { // nothing to do return []; } - this._onDidChangeDecorations.fire(); return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations); } }; @@ -1454,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(); @@ -1602,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 { @@ -1613,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); @@ -1646,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); } } @@ -1668,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); @@ -1693,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 { @@ -1705,15 +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, + semanticTokensApplied: tokens !== null, + ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }] + }); + } + + 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: ranges + semanticTokensApplied: true, + ranges: [{ fromLineNumber: changedRange.startLineNumber, toLineNumber: changedRange.endLineNumber }] }); } @@ -1727,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() @@ -1734,6 +1903,16 @@ export class TextModel extends Disposable implements model.ITextModel { }); } + public clearSemanticTokens(): void { + this._tokens2.flush(); + + this._emitModelTokensChangedEvent({ + tokenizationSupportChanged: false, + semanticTokensApplied: false, + ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() }] + }); + } + private _emitModelTokensChangedEvent(e: IModelTokensChangedEvent): void { if (!this._isDisposing) { this._onDidChangeTokens.fire(e); @@ -1772,7 +1951,8 @@ export class TextModel extends Disposable implements model.ITextModel { private _getLineTokens(lineNumber: number): LineTokens { const lineText = this.getLineContent(lineNumber); - return this._tokens.getTokens(this._languageIdentifier.id, lineNumber - 1, lineText); + const syntacticTokens = this._tokens.getTokens(this._languageIdentifier.id, lineNumber - 1, lineText); + return this._tokens2.addSemanticTokens(lineNumber, syntacticTokens); } public getLanguageIdentifier(): LanguageIdentifier { @@ -1899,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 { @@ -1909,6 +2089,7 @@ export class TextModel extends Disposable implements model.ITextModel { private _matchBracket(position: Position): [Range, Range] | null { const lineNumber = position.lineNumber; const lineTokens = this._getLineTokens(lineNumber); + const tokenCount = lineTokens.getCount(); const lineText = this._buffer.getLineContent(lineNumber); const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1); @@ -1921,6 +2102,15 @@ export class TextModel extends Disposable implements model.ITextModel { if (currentModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex))) { // limit search to not go before `maxBracketLength` let searchStartOffset = Math.max(0, position.column - 1 - currentModeBrackets.maxBracketLength); + for (let i = tokenIndex - 1; i >= 0; i--) { + const tokenEndOffset = lineTokens.getEndOffset(i); + if (tokenEndOffset <= searchStartOffset) { + break; + } + if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i))) { + searchStartOffset = tokenEndOffset; + } + } // limit search to not go after `maxBracketLength` const searchEndOffset = Math.min(lineText.length, position.column - 1 + currentModeBrackets.maxBracketLength); @@ -1937,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; } } @@ -1960,14 +2153,26 @@ export class TextModel extends Disposable implements model.ITextModel { if (prevModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(prevTokenIndex))) { // limit search in case previous token is very large, there's no need to go beyond `maxBracketLength` const searchStartOffset = Math.max(0, position.column - 1 - prevModeBrackets.maxBracketLength); - const searchEndOffset = Math.min(lineText.length, position.column - 1 + prevModeBrackets.maxBracketLength); + let searchEndOffset = Math.min(lineText.length, position.column - 1 + prevModeBrackets.maxBracketLength); + for (let i = prevTokenIndex + 1; i < tokenCount; i++) { + const tokenStartOffset = lineTokens.getStartOffset(i); + if (tokenStartOffset >= searchEndOffset) { + break; + } + if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i))) { + searchEndOffset = tokenStartOffset; + } + } const foundBracket = BracketsUtils.findPrevBracketInRange(prevModeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset); // 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; } } @@ -1977,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; @@ -2080,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; @@ -2318,19 +2533,38 @@ export class TextModel extends Disposable implements model.ITextModel { return null; } - public findEnclosingBrackets(_position: IPosition): [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(); let counts: number[] = []; - const resetCounts = (modeBrackets: RichEditBrackets | null) => { - counts = []; - for (let i = 0, len = modeBrackets ? modeBrackets.brackets.length : 0; i < len; i++) { - counts[i] = 0; + const resetCounts = (languageId: number, modeBrackets: RichEditBrackets | null) => { + if (!savedCounts.has(languageId)) { + let tmp = []; + for (let i = 0, len = modeBrackets ? modeBrackets.brackets.length : 0; i < len; i++) { + tmp[i] = 0; + } + savedCounts.set(languageId, tmp); } + 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; @@ -2346,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); } } @@ -2373,7 +2607,7 @@ export class TextModel extends Disposable implements model.ITextModel { if (languageId !== tokenLanguageId) { languageId = tokenLanguageId; modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId); - resetCounts(modeBrackets); + resetCounts(languageId, modeBrackets); } } @@ -2386,13 +2620,13 @@ 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; } languageId = tokenLanguageId; modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId); - resetCounts(modeBrackets); + resetCounts(languageId, modeBrackets); } const searchInToken = (!!modeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex))); @@ -2411,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); } } } @@ -2422,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); } } } @@ -2573,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) { @@ -2589,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) @@ -2604,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) @@ -2639,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 { @@ -2853,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); @@ -2868,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; } @@ -2890,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); @@ -2906,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); } @@ -2938,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; @@ -2948,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; @@ -2957,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; @@ -2990,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 { @@ -3005,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; } } @@ -3041,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 fc84cb84f9345..6511d02173f56 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 d41d1a2a1ba1f..8ebfdd22216d9 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 d965bcf8937ea..a49ef27a71e98 100644 --- a/src/vs/editor/common/model/tokensStore.ts +++ b/src/vs/editor/common/model/tokensStore.ts @@ -6,14 +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] { +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); @@ -24,21 +33,27 @@ export function countEOL(text: string): [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; } eolCount++; + lastLineStart = i + 1; } } if (eolCount === 0) { firstLineLength = text.length; } - return [eolCount, firstLineLength]; + return [eolCount, firstLineLength, text.length - lastLineStart, eol]; } function getDefaultMetadata(topLevelLanguageId: LanguageId): number { @@ -109,6 +124,570 @@ export class MultilineTokensBuilder { } } +export class SparseEncodedTokens { + /** + * The encoding of tokens is: + * 4*i deltaLine (from `startLineNumber`) + * 4*i+1 startCharacter (from the line start) + * 4*i+2 endCharacter (from the line start) + * 4*i+3 metadata + */ + private readonly _tokens: Uint32Array; + private _tokenCount: number; + + constructor(tokens: Uint32Array) { + this._tokens = tokens; + 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(); + if (tokenCount === 0) { + return -1; + } + return this._getDeltaLine(tokenCount - 1); + } + + 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; + } + + private _getDeltaLine(tokenIndex: number): number { + return this._tokens[4 * tokenIndex]; + } + + private _getStartCharacter(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 1]; + } + + private _getEndCharacter(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 2]; + } + + 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: + // + // 1. The token starts before the deletion range + // 1a. The token is completely before the deletion range + // ----------- + // xxxxxxxxxxx + // 1b. The token starts before, the deletion range ends after the token + // ----------- + // xxxxxxxxxxx + // 1c. The token starts before, the deletion range ends precisely with the token + // --------------- + // xxxxxxxx + // 1d. The token starts before, the deletion range is inside the token + // --------------- + // xxxxx + // + // 2. The token starts at the same position with the deletion range + // 2a. The token starts at the same position, and ends inside the deletion range + // ------- + // xxxxxxxxxxx + // 2b. The token starts at the same position, and ends at the same position as the deletion range + // ---------- + // xxxxxxxxxx + // 2c. The token starts at the same position, and ends after the deletion range + // ------------- + // xxxxxxx + // + // 3. The token starts inside the deletion range + // 3a. The token is inside the deletion range + // ------- + // xxxxxxxxxxxxx + // 3b. The token starts inside the deletion range, and ends at the same position as the deletion range + // ---------- + // xxxxxxxxxxxxx + // 3c. The token starts inside the deletion range, and ends after the deletion range + // ------------ + // xxxxxxxxxxx + // + // 4. The token starts after the deletion range + // ----------- + // xxxxxxxx + // + const tokens = this._tokens; + const tokenCount = this._tokenCount; + const deletedLineCount = (endDeltaLine - startDeltaLine); + let newTokenCount = 0; + let hasDeletedTokens = false; + for (let i = 0; i < tokenCount; i++) { + const srcOffset = 4 * i; + let tokenDeltaLine = tokens[srcOffset]; + let tokenStartCharacter = tokens[srcOffset + 1]; + let tokenEndCharacter = tokens[srcOffset + 2]; + const tokenMetadata = tokens[srcOffset + 3]; + + if (tokenDeltaLine < startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter <= startCharacter)) { + // 1a. The token is completely before the deletion range + // => nothing to do + newTokenCount++; + continue; + } else if (tokenDeltaLine === startDeltaLine && tokenStartCharacter < startCharacter) { + // 1b, 1c, 1d + // => the token survives, but it needs to shrink + if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { + // 1d. The token starts before, the deletion range is inside the token + // => the token shrinks by the deletion character count + tokenEndCharacter -= (endCharacter - startCharacter); + } else { + // 1b. The token starts before, the deletion range ends after the token + // 1c. The token starts before, the deletion range ends precisely with the token + // => the token shrinks its ending to the deletion start + tokenEndCharacter = startCharacter; + } + } else if (tokenDeltaLine === startDeltaLine && tokenStartCharacter === startCharacter) { + // 2a, 2b, 2c + if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { + // 2c. The token starts at the same position, and ends after the deletion range + // => the token shrinks by the deletion character count + tokenEndCharacter -= (endCharacter - startCharacter); + } else { + // 2a. The token starts at the same position, and ends inside the deletion range + // 2b. The token starts at the same position, and ends at the same position as the deletion range + // => the token is deleted + hasDeletedTokens = true; + continue; + } + } else if (tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter < endCharacter)) { + // 3a, 3b, 3c + if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { + // 3c. The token starts inside the deletion range, and ends after the deletion range + // => the token moves left and shrinks + if (tokenDeltaLine === startDeltaLine) { + // the deletion started on the same line as the token + // => the token moves left and shrinks + tokenStartCharacter = startCharacter; + tokenEndCharacter = tokenStartCharacter + (tokenEndCharacter - endCharacter); + } else { + // the deletion started on a line above the token + // => the token moves to the beginning of the line + tokenStartCharacter = 0; + tokenEndCharacter = tokenStartCharacter + (tokenEndCharacter - endCharacter); + } + } else { + // 3a. The token is inside the deletion range + // 3b. The token starts inside the deletion range, and ends at the same position as the deletion range + // => the token is deleted + hasDeletedTokens = true; + continue; + } + } else if (tokenDeltaLine > endDeltaLine) { + // 4. (partial) The token starts after the deletion range, on a line below... + if (deletedLineCount === 0 && !hasDeletedTokens) { + // early stop, there is no need to walk all the tokens and do nothing... + newTokenCount = tokenCount; + break; + } + tokenDeltaLine -= deletedLineCount; + } else if (tokenDeltaLine === endDeltaLine && tokenStartCharacter >= endCharacter) { + // 4. (continued) The token starts after the deletion range, on the last line where a deletion occurs + if (horizontalShiftForFirstLineTokens && tokenDeltaLine === 0) { + tokenStartCharacter += horizontalShiftForFirstLineTokens; + tokenEndCharacter += horizontalShiftForFirstLineTokens; + } + tokenDeltaLine -= deletedLineCount; + tokenStartCharacter -= (endCharacter - startCharacter); + tokenEndCharacter -= (endCharacter - startCharacter); + } else { + throw new Error(`Not possible!`); + } + + const destOffset = 4 * newTokenCount; + tokens[destOffset] = tokenDeltaLine; + tokens[destOffset + 1] = tokenStartCharacter; + tokens[destOffset + 2] = tokenEndCharacter; + tokens[destOffset + 3] = tokenMetadata; + newTokenCount++; + } + + this._tokenCount = newTokenCount; + } + + public acceptInsertText(deltaLine: number, character: number, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { + // Here are the cases I used to think about this: + // + // 1. The token is completely before the insertion point + // ----------- | + // 2. The token ends precisely at the insertion point + // -----------| + // 3. The token contains the insertion point + // -----|------ + // 4. The token starts precisely at the insertion point + // |----------- + // 5. The token is completely after the insertion point + // | ----------- + // + const isInsertingPreciselyOneWordCharacter = ( + eolCount === 0 + && firstLineLength === 1 + && ( + (firstCharCode >= CharCode.Digit0 && firstCharCode <= CharCode.Digit9) + || (firstCharCode >= CharCode.A && firstCharCode <= CharCode.Z) + || (firstCharCode >= CharCode.a && firstCharCode <= CharCode.z) + ) + ); + const tokens = this._tokens; + const tokenCount = this._tokenCount; + for (let i = 0; i < tokenCount; i++) { + const offset = 4 * i; + let tokenDeltaLine = tokens[offset]; + let tokenStartCharacter = tokens[offset + 1]; + let tokenEndCharacter = tokens[offset + 2]; + + if (tokenDeltaLine < deltaLine || (tokenDeltaLine === deltaLine && tokenEndCharacter < character)) { + // 1. The token is completely before the insertion point + // => nothing to do + continue; + } else if (tokenDeltaLine === deltaLine && tokenEndCharacter === character) { + // 2. The token ends precisely at the insertion point + // => expand the end character only if inserting precisely one character that is a word character + if (isInsertingPreciselyOneWordCharacter) { + tokenEndCharacter += 1; + } else { + continue; + } + } else if (tokenDeltaLine === deltaLine && tokenStartCharacter < character && character < tokenEndCharacter) { + // 3. The token contains the insertion point + if (eolCount === 0) { + // => just expand the end character + tokenEndCharacter += firstLineLength; + } else { + // => cut off the token + tokenEndCharacter = character; + } + } else { + // 4. or 5. + if (tokenDeltaLine === deltaLine && tokenStartCharacter === character) { + // 4. The token starts precisely at the insertion point + // => grow the token (by keeping its start constant) only if inserting precisely one character that is a word character + // => otherwise behave as in case 5. + if (isInsertingPreciselyOneWordCharacter) { + continue; + } + } + // => the token must move and keep its size constant + if (tokenDeltaLine === deltaLine) { + tokenDeltaLine += eolCount; + // this token is on the line where the insertion is taking place + if (eolCount === 0) { + tokenStartCharacter += firstLineLength; + tokenEndCharacter += firstLineLength; + } else { + const tokenLength = tokenEndCharacter - tokenStartCharacter; + tokenStartCharacter = lastLineLength + (tokenStartCharacter - character); + tokenEndCharacter = tokenStartCharacter + tokenLength; + } + } else { + tokenDeltaLine += eolCount; + } + } + + tokens[offset] = tokenDeltaLine; + tokens[offset + 1] = tokenStartCharacter; + tokens[offset + 2] = tokenEndCharacter; + } + } +} + +export class LineTokens2 { + + private readonly _tokens: Uint32Array; + + constructor(tokens: Uint32Array) { + this._tokens = tokens; + } + + public getCount(): number { + return this._tokens.length / 4; + } + + public getStartCharacter(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 1]; + } + + public getEndCharacter(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 2]; + } + + public getMetadata(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 3]; + } +} + +export class MultilineTokens2 { + + public startLineNumber: number; + public endLineNumber: number; + public tokens: SparseEncodedTokens; + + 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) { + return this.tokens.getLineTokens(lineNumber - this.startLineNumber); + } + return null; + } + + 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); + } + + public removeTokens(range: Range): void { + const startLineIndex = range.startLineNumber - this.startLineNumber; + const endLineIndex = range.endLineNumber - this.startLineNumber; + + 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 { + const [eolCount, firstLineLength, lastLineLength] = countEOL(text); + this.acceptEdit(range, eolCount, firstLineLength, lastLineLength, text.length > 0 ? text.charCodeAt(0) : CharCode.Null); + } + + public acceptEdit(range: IRange, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { + this._acceptDeleteRange(range); + this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength, lastLineLength, firstCharCode); + this._updateEndLineNumber(); + } + + private _acceptDeleteRange(range: IRange): void { + if (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn) { + // Nothing to delete + return; + } + + const firstLineIndex = range.startLineNumber - this.startLineNumber; + const lastLineIndex = range.endLineNumber - this.startLineNumber; + + if (lastLineIndex < 0) { + // this deletion occurs entirely before this block, so we only need to adjust line numbers + const deletedLinesCount = lastLineIndex - firstLineIndex; + this.startLineNumber -= deletedLinesCount; + return; + } + + const tokenMaxDeltaLine = this.tokens.getMaxDeltaLine(); + + if (firstLineIndex >= tokenMaxDeltaLine + 1) { + // this deletion occurs entirely after this block, so there is nothing to do + return; + } + + if (firstLineIndex < 0 && lastLineIndex >= tokenMaxDeltaLine + 1) { + // this deletion completely encompasses this block + this.startLineNumber = 0; + this.tokens.clear(); + return; + } + + if (firstLineIndex < 0) { + const deletedBefore = -firstLineIndex; + this.startLineNumber -= deletedBefore; + + this.tokens.acceptDeleteRange(range.startColumn - 1, 0, 0, lastLineIndex, range.endColumn - 1); + } else { + this.tokens.acceptDeleteRange(0, firstLineIndex, range.startColumn - 1, lastLineIndex, range.endColumn - 1); + } + } + + private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { + + if (eolCount === 0 && firstLineLength === 0) { + // Nothing to insert + return; + } + + const lineIndex = position.lineNumber - this.startLineNumber; + + if (lineIndex < 0) { + // this insertion occurs before this block, so we only need to adjust line numbers + this.startLineNumber += eolCount; + return; + } + + const tokenMaxDeltaLine = this.tokens.getMaxDeltaLine(); + + if (lineIndex >= tokenMaxDeltaLine + 1) { + // this insertion occurs after this block, so there is nothing to do + return; + } + + this.tokens.acceptInsertText(lineIndex, position.column - 1, eolCount, firstLineLength, lastLineLength, firstCharCode); + } +} + export class MultilineTokens { public startLineNumber: number; @@ -193,6 +772,7 @@ export class MultilineTokens { // this deletion completely encompasses this block this.startLineNumber = 0; this.tokens = []; + return; } if (firstLineIndex === lastLineIndex) { @@ -289,6 +869,227 @@ 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, 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 { + const pieces = this._pieces; + + if (pieces.length === 0) { + return aTokens; + } + + const pieceIndex = TokensStore2._findFirstPieceWithLine(pieces, lineNumber); + const bTokens = pieces[pieceIndex].getLineTokens(lineNumber); + + if (!bTokens) { + return aTokens; + } + + const aLen = aTokens.getCount(); + const bLen = bTokens.getCount(); + + 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) { + 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) { + emitToken(bStartCharacter, aTokens.getMetadata(aIndex)); + } + + // skip any tokens from `a` that are contained inside `b` + while (aIndex < aLen && aTokens.getEndOffset(aIndex) < bEndCharacter) { + emitToken(aTokens.getEndOffset(aIndex), (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask)); + aIndex++; + } + + 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` + emitToken(bEndCharacter, (aTokens.getMetadata(aMergeIndex) & aMask) | (bMetadata & bMask)); + } + } + + // push the remaining tokens from `a` + while (aIndex < aLen) { + emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex)); + aIndex++; + } + + return new LineTokens(new Uint32Array(result), aTokens.getLineContent()); + } + + private static _findFirstPieceWithLine(pieces: MultilineTokens2[], lineNumber: number): number { + let low = 0; + let high = pieces.length - 1; + + while (low < high) { + let mid = low + Math.floor((high - low) / 2); + + if (pieces[mid].endLineNumber < lineNumber) { + low = mid + 1; + } else if (pieces[mid].startLineNumber > lineNumber) { + high = mid - 1; + } else { + while (mid > low && pieces[mid - 1].startLineNumber <= lineNumber && lineNumber <= pieces[mid - 1].endLineNumber) { + mid--; + } + return mid; + } + } + + return low; + } + + //#region Editing + + public acceptEdit(range: IRange, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { + for (const piece of this._pieces) { + piece.acceptEdit(range, eolCount, firstLineLength, lastLineLength, firstCharCode); + } + } + + //#endregion +} + export class TokensStore { private _lineTokens: (Uint32Array | ArrayBuffer | null)[]; private _len: number; @@ -381,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 3e849b0dddf27..e857ddabb53dc 100644 --- a/src/vs/editor/common/model/wordHelper.ts +++ b/src/vs/editor/common/model/wordHelper.ts @@ -55,77 +55,82 @@ 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 - - let pos = column - 1 - textOffset; - let start = text.lastIndexOf(' ', pos - 1) + 1; - - wordDefinition.lastIndex = start; - 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 - }; +const _defaultConfig = { + maxLen: 1000, + windowSize: 15, + timeBudget: 150 +}; + +export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number, config = _defaultConfig): IWordAtPosition | null { + + 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) { + textOffset += column; + start = 0; + } else { + textOffset += start; } + text = text.substring(start, column + config.maxLen / 2); + return getWordAtText(column, wordDefinition, text, textOffset, config); } - return null; -} + const t1 = Date.now(); + const pos = column - 1 - textOffset; + let prevRegexIndex = -1; + let match: RegExpMatchArray | null = 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 + for (let i = 1; ; i++) { + // check time budget + if (Date.now() - t1 >= config.timeBudget) { + // break; + } - let pos = column - 1 - textOffset; - wordDefinition.lastIndex = 0; + // 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); - let match: RegExpMatchArray | null; - while (match = wordDefinition.exec(text)) { - const matchIndex = match.index || 0; - if (matchIndex > pos) { - // |nW -> matched only after the pos - return null; + if (!thisMatch && match) { + // stop: we have something + break; + } - } else if (wordDefinition.lastIndex >= pos) { - // W|W -> match encloses pos - return { - word: match[0], - startColumn: textOffset + 1 + matchIndex, - endColumn: textOffset + 1 + wordDefinition.lastIndex - }; + 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; } -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; +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 match; + } else if (stopPos > 0 && matchIndex > stopPos) { + 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; + return null; } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 67a01dd72cc78..3d34f627b1cf0 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,6 +18,7 @@ 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'; /** * Open ended enum at runtime @@ -125,6 +125,16 @@ export const enum MetadataConsts { FOREGROUND_MASK = 0b00000000011111111100000000000000, BACKGROUND_MASK = 0b11111111100000000000000000000000, + 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, FONT_STYLE_OFFSET = 11, @@ -255,6 +265,34 @@ 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. + */ +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. + */ +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, @@ -281,6 +319,8 @@ export const enum CompletionItemKind { Customcolor, Folder, TypeParameter, + User, + Issue, Snippet, // <- highest value (used for compare!) } @@ -289,35 +329,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'; + 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; }; })(); @@ -357,7 +405,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) { @@ -367,6 +416,28 @@ export let completionKindFromString: { }; })(); +export interface CompletionItemLabel { + /** + * The function or variable. Rendered leftmost. + */ + name: string; + + /** + * The parameters without the return type. Render after `name`. + */ + parameters?: string; + + /** + * The fully qualified name, like package name or file path. Rendered after `signature`. + */ + qualifier?: string; + + /** + * The return-type of a function or type of a property/variable. Rendered rightmost. + */ + type?: string; +} + export const enum CompletionItemTag { Deprecated = 1 } @@ -394,7 +465,7 @@ export interface CompletionItem { * this is also the text that is inserted when selecting * this completion. */ - label: string; + label: string | CompletionItemLabel; /** * The kind of this completion item. Based on the kind * an icon is chosen by the editor. @@ -536,7 +607,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 { @@ -546,13 +617,14 @@ export interface CodeAction { diagnostics?: IMarkerData[]; kind?: string; isPreferred?: boolean; + disabled?: string; } /** * @internal */ -export const enum CodeActionTrigger { - Automatic = 1, +export const enum CodeActionTriggerType { + Auto = 1, Manual = 2, } @@ -561,7 +633,7 @@ export const enum CodeActionTrigger { */ export interface CodeActionContext { only?: string; - trigger: CodeActionTrigger; + trigger: CodeActionTriggerType; } export interface CodeActionList extends IDisposable { @@ -574,6 +646,9 @@ export interface CodeActionList extends IDisposable { * @internal */ export interface CodeActionProvider { + + displayName?: string + /** * Provide commands for the given document and range. */ @@ -582,7 +657,14 @@ export interface CodeActionProvider { /** * Optional list of CodeActionKinds that this provider returns. */ - providedCodeActionKinds?: ReadonlyArray; + readonly providedCodeActionKinds?: ReadonlyArray; + + readonly documentation?: ReadonlyArray<{ readonly kind: string, readonly command: Command }>; + + /** + * @internal + */ + _getAdditionalMenuItems?(context: CodeActionContext, actions: readonly CodeAction[]): Command[]; } /** @@ -621,6 +703,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 @@ -718,6 +806,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. @@ -947,7 +1049,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}`; } } @@ -1175,11 +1283,11 @@ 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. + * Provides the folding ranges for a specific model. */ provideFoldingRanges(model: model.ITextModel, context: FoldingContext, token: CancellationToken): ProviderResult; } @@ -1228,34 +1336,37 @@ export class FoldingRangeKind { } } -/** - * @internal - */ -export function isResourceFileEdit(thing: any): thing is ResourceFileEdit { - return isObject(thing) && (Boolean((thing).newUri) || Boolean((thing).oldUri)); + +export interface WorkspaceEditMetadata { + needsConfirmation: boolean; + label: string; + description?: string; + iconPath?: { id: string } | URI | { light: URI, dark: URI }; } -/** - * @internal - */ -export function isResourceTextEdit(thing: any): thing is ResourceTextEdit { - return isObject(thing) && (thing).resource && Array.isArray((thing).edits); +export interface WorkspaceFileEditOptions { + overwrite?: boolean; + ignoreIfNotExists?: boolean; + ignoreIfExists?: boolean; + recursive?: boolean; } -export interface ResourceFileEdit { - oldUri: URI; - newUri: URI; - options: { overwrite?: boolean, ignoreIfNotExists?: boolean, ignoreIfExists?: boolean, recursive?: boolean }; +export interface WorkspaceFileEdit { + oldUri?: URI; + newUri?: URI; + options?: WorkspaceFileEditOptions; + metadata?: WorkspaceEditMetadata; } -export interface ResourceTextEdit { +export interface WorkspaceTextEdit { resource: URI; + edit: TextEdit; modelVersionId?: number; - edits: TextEdit[]; + metadata?: WorkspaceEditMetadata; } export interface WorkspaceEdit { - edits: Array; + edits: Array; } export interface Rejection { @@ -1271,6 +1382,35 @@ export interface RenameProvider { resolveRenameLocation?(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * @internal + */ +export interface AuthenticationSession { + id: string; + accessToken: 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 { id: string; @@ -1376,6 +1516,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 */ @@ -1432,7 +1587,7 @@ export interface IWebviewPortMapping { export interface IWebviewOptions { readonly enableScripts?: boolean; readonly enableCommandUris?: boolean; - readonly localResourceRoots?: ReadonlyArray; + readonly localResourceRoots?: ReadonlyArray; readonly portMapping?: ReadonlyArray; } @@ -1462,6 +1617,39 @@ export interface CodeLensProvider { resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; } +export interface SemanticTokensLegend { + readonly tokenTypes: string[]; + readonly tokenModifiers: string[]; +} + +export interface SemanticTokens { + readonly resultId?: string; + readonly data: Uint32Array; +} + +export interface SemanticTokensEdit { + readonly start: number; + readonly deleteCount: number; + readonly data?: Uint32Array; +} + +export interface SemanticTokensEdits { + readonly resultId?: string; + readonly edits: SemanticTokensEdit[]; +} + +export interface DocumentSemanticTokensProvider { + onDidChange?: Event; + getLegend(): SemanticTokensLegend; + provideDocumentSemanticTokens(model: model.ITextModel, lastResultId: string | null, token: CancellationToken): ProviderResult; + releaseDocumentSemanticTokens(resultId: string | undefined): void; +} + +export interface DocumentRangeSemanticTokensProvider { + getLegend(): SemanticTokensLegend; + provideDocumentRangeSemanticTokens(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult; +} + // --- feature registries ------ /** @@ -1489,6 +1677,11 @@ export const SignatureHelpProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const EvaluatableExpressionProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ @@ -1499,6 +1692,11 @@ export const DocumentSymbolProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const OnTypeRenameProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ @@ -1564,6 +1762,16 @@ export const SelectionRangeRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const DocumentSemanticTokensProviderRegistry = new LanguageFeatureRegistry(); + +/** + * @internal + */ +export const DocumentRangeSemanticTokensProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index 686c7a0dab200..98a3984944417 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -228,6 +228,28 @@ export interface EnterAction { removeText?: number; } +/** + * @internal + */ +export interface CompleteEnterAction { + /** + * Describe what to do with the indentation. + */ + indentAction: IndentAction; + /** + * Describes text to be appended after the new line and after the indentation. + */ + appendText: string; + /** + * Describes the number of characters to remove from the new line's indentation. + */ + removeText: number; + /** + * The line's indentation minus removeText + */ + indentation: string; +} + /** * @internal */ diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index cee0469e30e0e..daadeebb57e1f 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; @@ -12,13 +11,14 @@ 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 } from 'vs/editor/common/modes/languageConfiguration'; -import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; +import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional, CompleteEnterAction } 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'; import { IndentConsts, IndentRulesSupport } from 'vs/editor/common/modes/supports/indentRules'; -import { IOnEnterSupportOptions, OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; +import { OnEnterSupport } from 'vs/editor/common/modes/supports/onEnter'; import { RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; +import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; /** * Interface used to support insertion of mode specific comments. @@ -48,11 +48,11 @@ export class RichEditSupport { private readonly _languageIdentifier: LanguageIdentifier; private _brackets: RichEditBrackets | null; private _electricCharacter: BracketElectricCharacterSupport | null; + private readonly _onEnterSupport: OnEnterSupport | null; public readonly comments: ICommentsConfiguration | null; public readonly characterPair: CharacterPairSupport; public readonly wordDefinition: RegExp; - public readonly onEnter: OnEnterSupport | null; public readonly indentRulesSupport: IndentRulesSupport | null; public readonly indentationRules: IndentationRule | undefined; public readonly foldingRules: FoldingRules; @@ -70,8 +70,7 @@ export class RichEditSupport { this._conf = RichEditSupport._mergeConf(prev, rawConf); - this.onEnter = RichEditSupport._handleOnEnter(this._conf); - + this._onEnterSupport = (this._conf.brackets || this._conf.indentationRules || this._conf.onEnterRules ? new OnEnterSupport(this._conf) : null); this.comments = RichEditSupport._handleComments(this._conf); this.characterPair = new CharacterPairSupport(this._conf); @@ -102,6 +101,13 @@ export class RichEditSupport { return this._electricCharacter; } + public onEnter(autoIndent: EditorAutoIndentStrategy, oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null { + if (!this._onEnterSupport) { + return null; + } + return this._onEnterSupport.onEnter(autoIndent, oneLineAboveText, beforeEnterText, afterEnterText); + } + private static _mergeConf(prev: LanguageConfiguration | null, current: LanguageConfiguration): LanguageConfiguration { return { comments: (prev ? current.comments || prev.comments : current.comments), @@ -117,29 +123,6 @@ export class RichEditSupport { }; } - private static _handleOnEnter(conf: LanguageConfiguration): OnEnterSupport | null { - // on enter - let onEnter: IOnEnterSupportOptions = {}; - let empty = true; - - if (conf.brackets) { - empty = false; - onEnter.brackets = conf.brackets; - } - if (conf.indentationRules) { - empty = false; - } - if (conf.onEnterRules) { - empty = false; - onEnter.regExpRules = conf.onEnterRules; - } - - if (!empty) { - return new OnEnterSupport(onEnter); - } - return null; - } - private static _handleComments(conf: LanguageConfiguration): ICommentsConfiguration | null { let commentRule = conf.comments; if (!commentRule) { @@ -170,7 +153,7 @@ export class LanguageConfigurationChangeEvent { export class LanguageConfigurationRegistryImpl { - private readonly _entries = new Map(); + private readonly _entries = new Map(); private readonly _onDidChange = new Emitter(); public readonly onDidChange: Event = this._onDidChange.event; @@ -291,6 +274,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) { @@ -351,8 +344,12 @@ export class LanguageConfigurationRegistryImpl { * * This function only return the inherited indent based on above lines, it doesn't check whether current line should decrease or not. */ - public getInheritIndentForLine(model: IVirtualModel, lineNumber: number, honorIntentialIndent: boolean = true): { indentation: string; action: IndentAction | null; line?: number; } | null { - let indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id); + public getInheritIndentForLine(autoIndent: EditorAutoIndentStrategy, model: IVirtualModel, lineNumber: number, honorIntentialIndent: boolean = true): { indentation: string; action: IndentAction | null; line?: number; } | null { + if (autoIndent < EditorAutoIndentStrategy.Full) { + return null; + } + + const indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id); if (!indentRulesSupport) { return null; } @@ -364,7 +361,7 @@ export class LanguageConfigurationRegistryImpl { }; } - let precedingUnIgnoredLine = this.getPrecedingValidLine(model, lineNumber, indentRulesSupport); + const precedingUnIgnoredLine = this.getPrecedingValidLine(model, lineNumber, indentRulesSupport); if (precedingUnIgnoredLine < 0) { return null; } else if (precedingUnIgnoredLine < 1) { @@ -374,8 +371,7 @@ export class LanguageConfigurationRegistryImpl { }; } - let precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine); - + const precedingUnIgnoredLineContent = model.getLineContent(precedingUnIgnoredLine); if (indentRulesSupport.shouldIncrease(precedingUnIgnoredLineContent) || indentRulesSupport.shouldIndentNextLine(precedingUnIgnoredLineContent)) { return { indentation: strings.getLeadingWhitespace(precedingUnIgnoredLineContent), @@ -402,9 +398,9 @@ export class LanguageConfigurationRegistryImpl { }; } - let previousLine = precedingUnIgnoredLine - 1; + const previousLine = precedingUnIgnoredLine - 1; - let previousLineIndentMetadata = indentRulesSupport.getIndentMetadata(model.getLineContent(previousLine)); + const previousLineIndentMetadata = indentRulesSupport.getIndentMetadata(model.getLineContent(previousLine)); if (!(previousLineIndentMetadata & (IndentConsts.INCREASE_MASK | IndentConsts.DECREASE_MASK)) && (previousLineIndentMetadata & IndentConsts.INDENT_NEXTLINE_MASK)) { let stopLine = 0; @@ -432,7 +428,7 @@ export class LanguageConfigurationRegistryImpl { } else { // search from precedingUnIgnoredLine until we find one whose indent is not temporary for (let i = precedingUnIgnoredLine; i > 0; i--) { - let lineContent = model.getLineContent(i); + const lineContent = model.getLineContent(i); if (indentRulesSupport.shouldIncrease(lineContent)) { return { indentation: strings.getLeadingWhitespace(lineContent), @@ -472,27 +468,28 @@ export class LanguageConfigurationRegistryImpl { } } - public getGoodIndentForLine(virtualModel: IVirtualModel, languageId: LanguageId, lineNumber: number, indentConverter: IIndentConverter): string | null { - let indentRulesSupport = this.getIndentRulesSupport(languageId); + public getGoodIndentForLine(autoIndent: EditorAutoIndentStrategy, virtualModel: IVirtualModel, languageId: LanguageId, lineNumber: number, indentConverter: IIndentConverter): string | null { + if (autoIndent < EditorAutoIndentStrategy.Full) { + return null; + } + + const richEditSupport = this._getRichEditSupport(languageId); + if (!richEditSupport) { + return null; + } + + const indentRulesSupport = this.getIndentRulesSupport(languageId); if (!indentRulesSupport) { return null; } - let indent = this.getInheritIndentForLine(virtualModel, lineNumber); - let lineContent = virtualModel.getLineContent(lineNumber); + const indent = this.getInheritIndentForLine(autoIndent, virtualModel, lineNumber); + const lineContent = virtualModel.getLineContent(lineNumber); if (indent) { - let inheritLine = indent.line; + const inheritLine = indent.line; if (inheritLine !== undefined) { - let onEnterSupport = this._getOnEnterSupport(languageId); - let enterResult: EnterAction | null = null; - try { - if (onEnterSupport) { - enterResult = onEnterSupport.onEnter('', virtualModel.getLineContent(inheritLine), ''); - } - } catch (e) { - onUnexpectedError(e); - } + const enterResult = richEditSupport.onEnter(autoIndent, '', virtualModel.getLineContent(inheritLine), ''); if (enterResult) { let indentation = strings.getLeadingWhitespace(virtualModel.getLineContent(inheritLine)); @@ -539,16 +536,17 @@ export class LanguageConfigurationRegistryImpl { return null; } - public getIndentForEnter(model: ITextModel, range: Range, indentConverter: IIndentConverter, autoIndent: boolean): { beforeEnter: string, afterEnter: string } | null { + public getIndentForEnter(autoIndent: EditorAutoIndentStrategy, model: ITextModel, range: Range, indentConverter: IIndentConverter): { beforeEnter: string, afterEnter: string } | null { + if (autoIndent < EditorAutoIndentStrategy.Full) { + return null; + } model.forceTokenization(range.startLineNumber); - let lineTokens = model.getLineTokens(range.startLineNumber); - - let beforeEnterText; - let afterEnterText; - let scopedLineTokens = createScopedLineTokens(lineTokens, range.startColumn - 1); - let scopedLineText = scopedLineTokens.getLineContent(); + const lineTokens = model.getLineTokens(range.startLineNumber); + const scopedLineTokens = createScopedLineTokens(lineTokens, range.startColumn - 1); + const scopedLineText = scopedLineTokens.getLineContent(); let embeddedLanguage = false; + let beforeEnterText: string; if (scopedLineTokens.firstCharOffset > 0 && lineTokens.getLanguageId(0) !== scopedLineTokens.languageId) { // we are in the embeded language content embeddedLanguage = true; // if embeddedLanguage is true, then we don't touch the indentation of current line @@ -557,6 +555,7 @@ export class LanguageConfigurationRegistryImpl { beforeEnterText = lineTokens.getLineContent().substring(0, range.startColumn - 1); } + let afterEnterText: string; if (range.isEmpty()) { afterEnterText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset); } else { @@ -564,31 +563,15 @@ export class LanguageConfigurationRegistryImpl { afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset); } - let indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); - + const indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); if (!indentRulesSupport) { return null; } - let beforeEnterResult = beforeEnterText; - let beforeEnterIndent = strings.getLeadingWhitespace(beforeEnterText); - - if (!autoIndent && !embeddedLanguage) { - let beforeEnterIndentAction = this.getInheritIndentForLine(model, range.startLineNumber); + const beforeEnterResult = beforeEnterText; + const beforeEnterIndent = strings.getLeadingWhitespace(beforeEnterText); - if (indentRulesSupport.shouldDecrease(beforeEnterText)) { - if (beforeEnterIndentAction) { - beforeEnterIndent = beforeEnterIndentAction.indentation; - if (beforeEnterIndentAction.action !== IndentAction.Indent) { - beforeEnterIndent = indentConverter.unshiftIndent(beforeEnterIndent); - } - } - } - - beforeEnterResult = beforeEnterIndent + strings.ltrim(strings.ltrim(beforeEnterText, ' '), '\t'); - } - - let virtualModel: IVirtualModel = { + const virtualModel: IVirtualModel = { getLineTokens: (lineNumber: number) => { return model.getLineTokens(lineNumber); }, @@ -607,10 +590,10 @@ export class LanguageConfigurationRegistryImpl { } }; - let currentLineIndent = strings.getLeadingWhitespace(lineTokens.getLineContent()); - let afterEnterAction = this.getInheritIndentForLine(virtualModel, range.startLineNumber + 1); + const currentLineIndent = strings.getLeadingWhitespace(lineTokens.getLineContent()); + const afterEnterAction = this.getInheritIndentForLine(autoIndent, virtualModel, range.startLineNumber + 1); if (!afterEnterAction) { - let beforeEnter = embeddedLanguage ? currentLineIndent : beforeEnterIndent; + const beforeEnter = embeddedLanguage ? currentLineIndent : beforeEnterIndent; return { beforeEnter: beforeEnter, afterEnter: beforeEnter @@ -637,18 +620,21 @@ export class LanguageConfigurationRegistryImpl { * We should always allow intentional indentation. It means, if users change the indentation of `lineNumber` and the content of * this line doesn't match decreaseIndentPattern, we should not adjust the indentation. */ - public getIndentActionForType(model: ITextModel, range: Range, ch: string, indentConverter: IIndentConverter): string | null { - let scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); - let indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); + public getIndentActionForType(autoIndent: EditorAutoIndentStrategy, model: ITextModel, range: Range, ch: string, indentConverter: IIndentConverter): string | null { + if (autoIndent < EditorAutoIndentStrategy.Full) { + return null; + } + const scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); + const indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); if (!indentRulesSupport) { return null; } - let scopedLineText = scopedLineTokens.getLineContent(); - let beforeTypeText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset); - let afterTypeText; + const scopedLineText = scopedLineTokens.getLineContent(); + const beforeTypeText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset); // selection support + let afterTypeText: string; if (range.isEmpty()) { afterTypeText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset); } else { @@ -661,13 +647,12 @@ export class LanguageConfigurationRegistryImpl { if (!indentRulesSupport.shouldDecrease(beforeTypeText + afterTypeText) && indentRulesSupport.shouldDecrease(beforeTypeText + ch + afterTypeText)) { // after typing `ch`, the content matches decreaseIndentPattern, we should adjust the indent to a good manner. // 1. Get inherited indent action - let r = this.getInheritIndentForLine(model, range.startLineNumber, false); + const r = this.getInheritIndentForLine(autoIndent, model, range.startLineNumber, false); if (!r) { return null; } let indentation = r.indentation; - if (r.action !== IndentAction.Indent) { indentation = indentConverter.unshiftIndent(indentation); } @@ -679,15 +664,13 @@ export class LanguageConfigurationRegistryImpl { } public getIndentMetadata(model: ITextModel, lineNumber: number): number | null { - let indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id); + const indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id); if (!indentRulesSupport) { return null; } - if (lineNumber < 1 || lineNumber > model.getLineCount()) { return null; } - return indentRulesSupport.getIndentMetadata(model.getLineContent(lineNumber)); } @@ -695,34 +678,18 @@ export class LanguageConfigurationRegistryImpl { // begin onEnter - private _getOnEnterSupport(languageId: LanguageId): OnEnterSupport | null { - let value = this._getRichEditSupport(languageId); - if (!value) { + public getEnterAction(autoIndent: EditorAutoIndentStrategy, model: ITextModel, range: Range): CompleteEnterAction | null { + const scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); + const richEditSupport = this._getRichEditSupport(scopedLineTokens.languageId); + if (!richEditSupport) { return null; } - return value.onEnter || null; - } - public getRawEnterActionAtPosition(model: ITextModel, lineNumber: number, column: number): EnterAction | null { - let r = this.getEnterAction(model, new Range(lineNumber, column, lineNumber, column)); - - return r ? r.enterAction : null; - } - - public getEnterAction(model: ITextModel, range: Range): { enterAction: EnterAction; indentation: string; } | null { - let indentation = this.getIndentationAtPosition(model, range.startLineNumber, range.startColumn); - - let scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); - let onEnterSupport = this._getOnEnterSupport(scopedLineTokens.languageId); - if (!onEnterSupport) { - return null; - } - - let scopedLineText = scopedLineTokens.getLineContent(); - let beforeEnterText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset); - let afterEnterText; + const scopedLineText = scopedLineTokens.getLineContent(); + const beforeEnterText = scopedLineText.substr(0, range.startColumn - 1 - scopedLineTokens.firstCharOffset); // selection support + let afterEnterText: string; if (range.isEmpty()) { afterEnterText = scopedLineText.substr(range.startColumn - 1 - scopedLineTokens.firstCharOffset); } else { @@ -730,73 +697,70 @@ export class LanguageConfigurationRegistryImpl { afterEnterText = endScopedLineTokens.getLineContent().substr(range.endColumn - 1 - scopedLineTokens.firstCharOffset); } - let lineNumber = range.startLineNumber; let oneLineAboveText = ''; - - if (lineNumber > 1 && scopedLineTokens.firstCharOffset === 0) { + if (range.startLineNumber > 1 && scopedLineTokens.firstCharOffset === 0) { // This is not the first line and the entire line belongs to this mode - let oneLineAboveScopedLineTokens = this.getScopedLineTokens(model, lineNumber - 1); + const oneLineAboveScopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber - 1); if (oneLineAboveScopedLineTokens.languageId === scopedLineTokens.languageId) { // The line above ends with text belonging to the same mode oneLineAboveText = oneLineAboveScopedLineTokens.getLineContent(); } } - let enterResult: EnterAction | null = null; - try { - enterResult = onEnterSupport.onEnter(oneLineAboveText, beforeEnterText, afterEnterText); - } catch (e) { - onUnexpectedError(e); - } - + const enterResult = richEditSupport.onEnter(autoIndent, oneLineAboveText, beforeEnterText, afterEnterText); if (!enterResult) { return null; - } else { - // Here we add `\t` to appendText first because enterAction is leveraging appendText and removeText to change indentation. - if (!enterResult.appendText) { - if ( - (enterResult.indentAction === IndentAction.Indent) || - (enterResult.indentAction === IndentAction.IndentOutdent) - ) { - enterResult.appendText = '\t'; - } else { - enterResult.appendText = ''; - } + } + + const indentAction = enterResult.indentAction; + let appendText = enterResult.appendText; + const removeText = enterResult.removeText || 0; + + // Here we add `\t` to appendText first because enterAction is leveraging appendText and removeText to change indentation. + if (!appendText) { + if ( + (indentAction === IndentAction.Indent) || + (indentAction === IndentAction.IndentOutdent) + ) { + appendText = '\t'; + } else { + appendText = ''; } } - if (enterResult.removeText) { - indentation = indentation.substring(0, indentation.length - enterResult.removeText); + let indentation = this.getIndentationAtPosition(model, range.startLineNumber, range.startColumn); + if (removeText) { + indentation = indentation.substring(0, indentation.length - removeText); } return { - enterAction: enterResult, - indentation: indentation, + indentAction: indentAction, + appendText: appendText, + removeText: removeText, + indentation: indentation }; } public getIndentationAtPosition(model: ITextModel, lineNumber: number, column: number): string { - let lineText = model.getLineContent(lineNumber); + const lineText = model.getLineContent(lineNumber); let indentation = strings.getLeadingWhitespace(lineText); if (indentation.length > column - 1) { indentation = indentation.substring(0, column - 1); } - return indentation; } - private getScopedLineTokens(model: ITextModel, lineNumber: number, columnNumber?: number) { + private getScopedLineTokens(model: ITextModel, lineNumber: number, columnNumber?: number): ScopedLineTokens { model.forceTokenization(lineNumber); - let lineTokens = model.getLineTokens(lineNumber); - let column = (typeof columnNumber === 'undefined' ? model.getLineMaxColumn(lineNumber) - 1 : columnNumber - 1); - let scopedLineTokens = createScopedLineTokens(lineTokens, column); - return scopedLineTokens; + const lineTokens = model.getLineTokens(lineNumber); + const column = (typeof columnNumber === 'undefined' ? model.getLineMaxColumn(lineNumber) - 1 : columnNumber - 1); + return createScopedLineTokens(lineTokens, column); } // end onEnter public getBracketsSupport(languageId: LanguageId): RichEditBrackets | null { - let value = this._getRichEditSupport(languageId); + const value = this._getRichEditSupport(languageId); if (!value) { return null; } diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index 4278ec10e3a8a..b5cd1ca4a75ef 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 4cf93dc4d2345..f3bbac42f8e84 100644 --- a/src/vs/editor/common/modes/languageSelector.ts +++ b/src/vs/editor/common/modes/languageSelector.ts @@ -5,6 +5,7 @@ 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; @@ -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 9d38173653648..c0ca979fb9b94 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 f91dd75d17c82..c2ef63388de5c 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.ts b/src/vs/editor/common/modes/supports.ts index b8cb5201ca770..f6de5f27172f5 100644 --- a/src/vs/editor/common/modes/supports.ts +++ b/src/vs/editor/common/modes/supports.ts @@ -62,6 +62,11 @@ export class ScopedLineTokens { return actualLineContent.substring(this.firstCharOffset, this._lastCharOffset); } + public getActualLineContentBefore(offset: number): string { + const actualLineContent = this._actual.getLineContent(); + return actualLineContent.substring(0, this.firstCharOffset + offset); + } + public getTokenCount(): number { return this._lastTokenIndex - this._firstTokenIndex; } diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index 9dee3d4ac889a..ead8e6da3a68a 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -49,28 +49,27 @@ export class BracketElectricCharacterSupport { return null; } - let tokenIndex = context.findTokenIndexAtOffset(column - 1); + const tokenIndex = context.findTokenIndexAtOffset(column - 1); if (ignoreBracketsInToken(context.getStandardTokenType(tokenIndex))) { return null; } - let reversedBracketRegex = this._richEditBrackets.reversedRegex; - let text = context.getLineContent().substring(0, column - 1) + character; + const reversedBracketRegex = this._richEditBrackets.reversedRegex; + const text = context.getLineContent().substring(0, column - 1) + character; - let r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, 1, text, 0, text.length); + const r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, 1, text, 0, text.length); if (!r) { return null; } - let bracketText = text.substring(r.startColumn - 1, r.endColumn - 1); - bracketText = bracketText.toLowerCase(); + const bracketText = text.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase(); - let isOpen = this._richEditBrackets.textIsOpenBracket[bracketText]; + const isOpen = this._richEditBrackets.textIsOpenBracket[bracketText]; if (isOpen) { return null; } - let textBeforeBracket = text.substring(0, r.startColumn - 1); + const textBeforeBracket = context.getActualLineContentBefore(r.startColumn - 1); if (!/^\s*$/.test(textBeforeBracket)) { // There is other text on the line before the bracket return null; diff --git a/src/vs/editor/common/modes/supports/onEnter.ts b/src/vs/editor/common/modes/supports/onEnter.ts index 6021d4bf8e377..f42cc4a4da22e 100644 --- a/src/vs/editor/common/modes/supports/onEnter.ts +++ b/src/vs/editor/common/modes/supports/onEnter.ts @@ -6,10 +6,11 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; import { CharacterPair, EnterAction, IndentAction, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration'; +import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; export interface IOnEnterSupportOptions { brackets?: CharacterPair[]; - regExpRules?: OnEnterRule[]; + onEnterRules?: OnEnterRule[]; } interface IProcessedBracketPair { @@ -24,7 +25,7 @@ export class OnEnterSupport { private readonly _brackets: IProcessedBracketPair[]; private readonly _regExpRules: OnEnterRule[]; - constructor(opts?: IOnEnterSupportOptions) { + constructor(opts: IOnEnterSupportOptions) { opts = opts || {}; opts.brackets = opts.brackets || [ ['(', ')'], @@ -45,49 +46,54 @@ export class OnEnterSupport { }); } }); - this._regExpRules = opts.regExpRules || []; + this._regExpRules = opts.onEnterRules || []; } - public onEnter(oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null { + public onEnter(autoIndent: EditorAutoIndentStrategy, oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null { // (1): `regExpRules` - for (let i = 0, len = this._regExpRules.length; i < len; i++) { - let rule = this._regExpRules[i]; - const regResult = [{ - reg: rule.beforeText, - text: beforeEnterText - }, { - reg: rule.afterText, - text: afterEnterText - }, { - reg: rule.oneLineAboveText, - text: oneLineAboveText - }].every((obj): boolean => { - return obj.reg ? obj.reg.test(obj.text) : true; - }); - - if (regResult) { - return rule.action; + if (autoIndent >= EditorAutoIndentStrategy.Advanced) { + for (let i = 0, len = this._regExpRules.length; i < len; i++) { + let rule = this._regExpRules[i]; + const regResult = [{ + reg: rule.beforeText, + text: beforeEnterText + }, { + reg: rule.afterText, + text: afterEnterText + }, { + reg: rule.oneLineAboveText, + text: oneLineAboveText + }].every((obj): boolean => { + return obj.reg ? obj.reg.test(obj.text) : true; + }); + + if (regResult) { + return rule.action; + } } } - // (2): Special indent-outdent - if (beforeEnterText.length > 0 && afterEnterText.length > 0) { - for (let i = 0, len = this._brackets.length; i < len; i++) { - let bracket = this._brackets[i]; - if (bracket.openRegExp.test(beforeEnterText) && bracket.closeRegExp.test(afterEnterText)) { - return { indentAction: IndentAction.IndentOutdent }; + if (autoIndent >= EditorAutoIndentStrategy.Brackets) { + if (beforeEnterText.length > 0 && afterEnterText.length > 0) { + for (let i = 0, len = this._brackets.length; i < len; i++) { + let bracket = this._brackets[i]; + if (bracket.openRegExp.test(beforeEnterText) && bracket.closeRegExp.test(afterEnterText)) { + return { indentAction: IndentAction.IndentOutdent }; + } } } } // (4): Open bracket based logic - if (beforeEnterText.length > 0) { - for (let i = 0, len = this._brackets.length; i < len; i++) { - let bracket = this._brackets[i]; - if (bracket.openRegExp.test(beforeEnterText)) { - return { indentAction: IndentAction.Indent }; + if (autoIndent >= EditorAutoIndentStrategy.Brackets) { + if (beforeEnterText.length > 0) { + for (let i = 0, len = this._brackets.length; i < len; i++) { + let bracket = this._brackets[i]; + if (bracket.openRegExp.test(beforeEnterText)) { + return { indentAction: IndentAction.Indent }; + } } } } diff --git a/src/vs/editor/common/modes/supports/richEditBrackets.ts b/src/vs/editor/common/modes/supports/richEditBrackets.ts index ae10537c82e68..9325b4814a0fc 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 12566c3b00e7b..e28c23d39b465 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 e98f2b37f1952..dd64e79ded683 100644 --- a/src/vs/editor/common/modes/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/modes/textToHtmlTokenizer.ts @@ -46,7 +46,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; tabsCharDelta += insertSpacesCount - 1; while (insertSpacesCount > 0) { - partContent += useNbsp ? ' ' : ' '; + partContent += useNbsp ? ' ' : ' '; insertSpacesCount--; } break; @@ -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; @@ -78,7 +80,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens break; case CharCode.Space: - partContent += useNbsp ? ' ' : ' '; + partContent += useNbsp ? ' ' : ' '; break; default: diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index fc3423047fb6f..c92c1296b1959 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 01ca792e4970d..5a8391e91afee 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'; @@ -13,7 +12,7 @@ import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { DiffComputer } from 'vs/editor/common/diff/diffComputer'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IChange } from 'vs/editor/common/editorCommon'; import { EndOfLineSequence, IWordAtPosition } from 'vs/editor/common/model'; import { IModelChangedEvent, MirrorTextModel as BaseMirrorModel } from 'vs/editor/common/model/mirrorTextModel'; import { ensureValidWordDefinition, getWordAtText } from 'vs/editor/common/model/wordHelper'; @@ -65,7 +64,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 +152,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 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 { + *[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 { next }; } public getLineWords(lineNumber: number, wordDefinition: RegExp): IWordAtPosition[] { @@ -322,7 +322,7 @@ export interface IForeignModuleFactory { (ctx: IWorkerContext, createData: any): any; } -declare var require: any; +declare const require: any; /** * @internal @@ -419,7 +419,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { return true; } - public async computeDirtyDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): Promise { + public async computeDirtyDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): Promise { let original = this._getModel(originalUrl); let modified = this._getModel(modifiedUrl); if (!original || !modified) { @@ -545,12 +545,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,6 +554,9 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { continue; } words.push(word); + if (seen.size > EditorSimpleWorker._suggestionsLimit) { + break; + } } return words; } diff --git a/src/vs/editor/common/services/editorWorkerService.ts b/src/vs/editor/common/services/editorWorkerService.ts index ab850e96a2c3c..a99c84822d876 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 f2061ba0b4c27..3ee2f5ad18274 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -10,18 +10,19 @@ import { SimpleWorkerClient, logOnceWebWorkerWarning, IWorkerClient } from 'vs/b import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IChange } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import * as modes from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker'; import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; 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)) { @@ -90,7 +93,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified)); } - public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { + public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { return this._workerManager.withWorker().then(client => client.computeDirtyDiff(original, modified, ignoreTrimWhitespace)); } @@ -236,7 +239,7 @@ class WorkerManager extends Disposable { public withWorker(): Promise { this._lastWorkerUsedTime = (new Date()).getTime(); if (!this._editorWorkerClient) { - this._editorWorkerClient = new EditorWorkerClient(this._modelService, 'editorWorkerService'); + this._editorWorkerClient = new EditorWorkerClient(this._modelService, false, 'editorWorkerService'); } return Promise.resolve(this._editorWorkerClient); } @@ -374,13 +377,16 @@ export class EditorWorkerHost { export class EditorWorkerClient extends Disposable { private readonly _modelService: IModelService; + private readonly _keepIdleModels: boolean; private _worker: IWorkerClient | null; private readonly _workerFactory: DefaultWorkerFactory; private _modelManager: EditorModelManager | null; + private _disposed = false; - constructor(modelService: IModelService, label: string | undefined) { + constructor(modelService: IModelService, keepIdleModels: boolean, label: string | undefined) { super(); this._modelService = modelService; + this._keepIdleModels = keepIdleModels; this._workerFactory = new DefaultWorkerFactory(label); this._worker = null; this._modelManager = null; @@ -417,12 +423,15 @@ export class EditorWorkerClient extends Disposable { private _getOrCreateModelManager(proxy: EditorSimpleWorker): EditorModelManager { if (!this._modelManager) { - this._modelManager = this._register(new EditorModelManager(proxy, this._modelService, false)); + this._modelManager = this._register(new EditorModelManager(proxy, this._modelService, this._keepIdleModels)); } return this._modelManager; } protected _withSyncedResources(resources: URI[]): Promise { + if (this._disposed) { + return Promise.reject(canceled()); + } return this._getProxy().then((proxy) => { this._getOrCreateModelManager(proxy).ensureSyncedResources(resources); return proxy; @@ -435,7 +444,7 @@ export class EditorWorkerClient extends Disposable { }); } - public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { + public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise { return this._withSyncedResources([original, modified]).then(proxy => { return proxy.computeDirtyDiff(original.toString(), modified.toString(), ignoreTrimWhitespace); }); @@ -491,4 +500,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 fbdc31d6d5c2b..b20a0b7c58bfe 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 8a12a471f9580..c734735a733fc 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 fbf9037fa919f..cb80908b64960 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IMarkerService, IMarker, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IModelDeltaDecoration, ITextModel, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, IModelDecoration, MinimapPosition, IModelDecorationMinimapOptions } from 'vs/editor/common/model'; import { ClassName } from 'vs/editor/common/model/intervalTree'; @@ -12,11 +12,11 @@ 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'; +import { Delayer } from 'vs/base/common/async'; function MODEL_ID(resource: URI): string { return resource.toString(); @@ -31,16 +31,23 @@ 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 ids = this.model.deltaDecorations(keys(this._markersData), newDecorations); + register(t: T): T { + return super._register(t); + } + + 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 { @@ -61,7 +68,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; @@ -87,7 +94,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][] { @@ -107,6 +114,8 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor private _onModelAdded(model: ITextModel): void { const markerDecorations = new MarkerDecorations(model); this._markerDecorations.set(MODEL_ID(model.uri), markerDecorations); + const delayer = markerDecorations.register(new Delayer(100)); + markerDecorations.register(model.onDidChangeContent(() => delayer.trigger(() => this._updateDecorations(markerDecorations)))); this._updateDecorations(markerDecorations); } @@ -136,20 +145,19 @@ 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 { let ret = Range.lift(rawMarker); - if (rawMarker.severity === MarkerSeverity.Hint) { - if (!rawMarker.tags || rawMarker.tags.indexOf(MarkerTag.Unnecessary) === -1) { - // * never render hints on multiple lines - // * make enough space for three dots - ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2); - } + if (rawMarker.severity === MarkerSeverity.Hint && !this._hasMarkerTag(rawMarker, MarkerTag.Unnecessary) && !this._hasMarkerTag(rawMarker, MarkerTag.Deprecated)) { + // * never render hints on multiple lines + // * make enough space for three dots + ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2); } ret = model.validateRange(ret); @@ -185,7 +193,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor private _createDecorationOption(marker: IMarker): IModelDecorationOptions { - let className: string; + let className: string | undefined; let color: ThemeColor | undefined = undefined; let zIndex: number; let inlineClassName: string | undefined = undefined; @@ -193,7 +201,9 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor switch (marker.severity) { case MarkerSeverity.Hint: - if (marker.tags && marker.tags.indexOf(MarkerTag.Unnecessary) >= 0) { + if (this._hasMarkerTag(marker, MarkerTag.Deprecated)) { + className = undefined; + } else if (this._hasMarkerTag(marker, MarkerTag.Unnecessary)) { className = ClassName.EditorUnnecessaryDecoration; } else { className = ClassName.EditorHintDecoration; @@ -205,7 +215,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor color = themeColorFromId(overviewRulerWarning); zIndex = 20; minimap = { - color, + color: themeColorFromId(minimapWarning), position: MinimapPosition.Inline }; break; @@ -220,7 +230,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor color = themeColorFromId(overviewRulerError); zIndex = 30; minimap = { - color, + color: themeColorFromId(minimapError), position: MinimapPosition.Inline }; break; @@ -248,4 +258,11 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor inlineClassName, }; } + + private _hasMarkerTag(marker: IMarker, tag: MarkerTag): boolean { + if (marker.tags) { + return marker.tags.indexOf(tag) >= 0; + } + return false; + } } diff --git a/src/vs/editor/common/services/markersDecorationService.ts b/src/vs/editor/common/services/markersDecorationService.ts index cd1c541cbc0b7..745260c8884b5 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 a6d2a6bc9e273..2739d65255175 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 083d387118b7b..6b2fd6f80f8f1 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 1c4a451440417..ab583a7390c65 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 5b5070184eae1..576a7dd4ce863 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -4,28 +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 } 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 } from 'vs/editor/common/model/textModelEvents'; -import { LanguageIdentifier } from 'vs/editor/common/modes'; +import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; +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 { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; +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 { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IUndoRedoService, IUndoRedoElement, IPastFutureElements, ResourceEditStackSnapshot } from 'vs/platform/undoRedo/common/undoRedo'; +import { StringSHA1 } from 'vs/base/common/hash'; +import { SingleModelEditStackElement, MultiModelEditStackElement, 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; @@ -33,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 ) { @@ -87,12 +113,46 @@ interface IRawConfig { const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? DefaultEndOfLine.LF : DefaultEndOfLine.CRLF; +export interface EditStackPastFutureElements { + past: EditStackElement[]; + future: EditStackElement[]; +} + +export function isEditStackPastFutureElements(undoElements: IPastFutureElements): undoElements is EditStackPastFutureElements { + return (isEditStackElements(undoElements.past) && isEditStackElements(undoElements.future)); +} + +function isEditStackElements(elements: IUndoRedoElement[]): elements is EditStackElement[] { + for (const element of elements) { + if (element instanceof SingleModelEditStackElement) { + continue; + } + if (element instanceof MultiModelEditStackElement) { + continue; + } + return false; + } + return true; +} + +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, + ) { } +} + export class ModelServiceImpl extends Disposable implements IModelService { - public _serviceBrand: undefined; - private readonly _configurationService: IConfigurationService; - private readonly _configurationServiceSubscription: IDisposable; - private readonly _resourcePropertiesService: ITextResourcePropertiesService; + 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; @@ -103,33 +163,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 + @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, 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; } @@ -140,7 +207,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; } @@ -189,11 +256,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; } @@ -201,14 +287,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]; @@ -248,17 +334,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]) { @@ -291,7 +433,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { model.pushEditOperations( [], ModelServiceImpl._computeEdits(model, textBuffer), - (inverseEditOperations: IIdentifiedSingleEditOperation[]) => [] + () => [] ); model.pushStackElement(); } @@ -331,7 +473,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); @@ -365,7 +508,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; } @@ -374,19 +517,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() && (resource.scheme === Schemas.file || resource.scheme === Schemas.vscodeRemote || resource.scheme === Schemas.userData))) { + 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); } @@ -394,19 +589,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(); @@ -430,3 +629,333 @@ export class ModelServiceImpl extends Disposable implements IModelService { 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 readonly _watchers: Record; + private readonly _semanticStyling: SemanticStyling; + + constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, semanticStyling: SemanticStyling) { + super(); + this._watchers = Object.create(null); + this._semanticStyling = semanticStyling; + + const register = (model: ITextModel) => { + this._watchers[model.uri.toString()] = new ModelSemanticColoring(model, themeService, this._semanticStyling); + }; + const deregister = (model: ITextModel, modelSemanticColoring: ModelSemanticColoring) => { + 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, themeService, configurationService)) { + register(model); + } + })); + this._register(modelService.onModelRemoved((model) => { + const curr = this._watchers[model.uri.toString()]; + 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; + + constructor( + private readonly _themeService: IThemeService, + private readonly _logService: ILogService + ) { + super(); + this._caches = new WeakMap(); + this._register(this._themeService.onDidColorThemeChange(() => { + this._caches = new WeakMap(); + })); + } + + public get(provider: DocumentTokensProvider): SemanticTokensProviderStyling { + if (!this._caches.has(provider)) { + this._caches.set(provider, new SemanticTokensProviderStyling(provider.getLegend(), this._themeService, this._logService)); + } + return this._caches.get(provider)!; + } +} + +class SemanticTokensResponse { + constructor( + private readonly _provider: DocumentSemanticTokensProvider, + public readonly resultId: string | undefined, + public readonly data: Uint32Array + ) { } + + public dispose(): void { + this._provider.releaseDocumentSemanticTokens(this.resultId); + } +} + +class ModelSemanticColoring extends Disposable { + + private _isDisposed: boolean; + private readonly _model: ITextModel; + private readonly _semanticStyling: SemanticStyling; + private readonly _fetchDocumentSemanticTokens: RunOnceScheduler; + private _currentDocumentResponse: SemanticTokensResponse | null; + private _currentDocumentRequestCancellationTokenSource: CancellationTokenSource | null; + private _documentProvidersChangeListeners: IDisposable[]; + + constructor(model: ITextModel, themeService: IThemeService, stylingProvider: SemanticStyling) { + super(); + + this._isDisposed = false; + this._model = model; + this._semanticStyling = stylingProvider; + this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), 300)); + this._currentDocumentResponse = null; + this._currentDocumentRequestCancellationTokenSource = null; + this._documentProvidersChangeListeners = []; + + this._register(this._model.onDidChangeContent(() => { + if (!this._fetchDocumentSemanticTokens.isScheduled()) { + this._fetchDocumentSemanticTokens.schedule(); + } + })); + 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._currentDocumentResponse) { + this._currentDocumentResponse.dispose(); + this._currentDocumentResponse = null; + } + if (this._currentDocumentRequestCancellationTokenSource) { + this._currentDocumentRequestCancellationTokenSource.cancel(); + this._currentDocumentRequestCancellationTokenSource = null; + } + this._setDocumentSemanticTokens(null, null, null, []); + this._isDisposed = true; + + super.dispose(); + } + + private _fetchDocumentSemanticTokensNow(): void { + if (this._currentDocumentRequestCancellationTokenSource) { + // there is already a request running, let it finish... + return; + } + const provider = this._getSemanticColoringProvider(); + if (!provider) { + return; + } + this._currentDocumentRequestCancellationTokenSource = new CancellationTokenSource(); + + const pendingChanges: IModelContentChangedEvent[] = []; + const contentChangeListener = this._model.onDidChangeContent((e) => { + pendingChanges.push(e); + }); + + const styling = this._semanticStyling.get(provider); + + 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._currentDocumentRequestCancellationTokenSource = null; + contentChangeListener.dispose(); + this._setDocumentSemanticTokens(provider, res || null, styling, pendingChanges); + }, (err) => { + 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(); + + if (pendingChanges.length > 0) { + // More changes occurred while the request was running + if (!this._fetchDocumentSemanticTokens.isScheduled()) { + this._fetchDocumentSemanticTokens.schedule(); + } + } + }); + } + + private static _isSemanticTokens(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokens { + return v && !!((v).data); + } + + private static _isSemanticTokensEdits(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokensEdits { + return v && Array.isArray((v).edits); + } + + private static _copy(src: Uint32Array, srcOffset: number, dest: Uint32Array, destOffset: number, length: number): void { + for (let i = 0; i < length; i++) { + dest[destOffset + i] = src[srcOffset + i]; + } + } + + 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! + if (provider && tokens) { + provider.releaseDocumentSemanticTokens(tokens.resultId); + } + return; + } + 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, true); + return; + } + if (tokens.edits.length === 0) { + // nothing to do! + tokens = { + resultId: tokens.resultId, + data: currentResponse.data + }; + } else { + let deltaLength = 0; + for (const edit of tokens.edits) { + deltaLength += (edit.data ? edit.data.length : 0) - edit.deleteCount; + } + + const srcData = currentResponse.data; + const destData = new Uint32Array(srcData.length + deltaLength); + + let srcLastStart = srcData.length; + let destLastStart = destData.length; + for (let i = tokens.edits.length - 1; i >= 0; i--) { + const edit = tokens.edits[i]; + + const copyCount = srcLastStart - (edit.start + edit.deleteCount); + if (copyCount > 0) { + ModelSemanticColoring._copy(srcData, srcLastStart - copyCount, destData, destLastStart - copyCount, copyCount); + destLastStart -= copyCount; + } + + if (edit.data) { + ModelSemanticColoring._copy(edit.data, 0, destData, destLastStart - edit.data.length, edit.data.length); + destLastStart -= edit.data.length; + } + + srcLastStart = edit.start; + } + + if (srcLastStart > 0) { + ModelSemanticColoring._copy(srcData, 0, destData, 0, srcLastStart); + } + + tokens = { + resultId: tokens.resultId, + data: destData + }; + } + } + + if (ModelSemanticColoring._isSemanticTokens(tokens)) { + + this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data); + + const result = toMultilineTokens2(tokens, styling, this._model.getLanguageIdentifier()); + + // Adjust incoming semantic tokens + if (pendingChanges.length > 0) { + // More changes occurred while the request was running + // We need to: + // 1. Adjust incoming semantic tokens + // 2. Request them again + for (const change of pendingChanges) { + for (const area of result) { + for (const singleChange of change.changes) { + area.applyEdit(singleChange.range, singleChange.text); + } + } + } + + if (!this._fetchDocumentSemanticTokens.isScheduled()) { + this._fetchDocumentSemanticTokens.schedule(); + } + } + + this._model.setSemanticTokens(result, true); + return; + } + + this._model.setSemanticTokens(null, true); + } + + private _getSemanticColoringProvider(): DocumentSemanticTokensProvider | null { + const result = DocumentSemanticTokensProviderRegistry.ordered(this._model); + return (result.length > 0 ? result[0] : null); + } +} diff --git a/src/vs/editor/common/services/modelUndoRedoParticipant.ts b/src/vs/editor/common/services/modelUndoRedoParticipant.ts new file mode 100644 index 0000000000000..0862ccf8d6e26 --- /dev/null +++ b/src/vs/editor/common/services/modelUndoRedoParticipant.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * 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, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo'; +import { isEditStackPastFutureElements } from 'vs/editor/common/services/modelServiceImpl'; +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; + } + if (!isEditStackPastFutureElements(elements)) { + return; + } + for (const element of elements.past) { + if (element.type === UndoRedoElementType.Workspace) { + element.setDelegate(this); + } + } + for (const element of elements.future) { + if (element.type === UndoRedoElementType.Workspace) { + 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 b425ebb8fde77..74470a810177b 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 { @@ -52,7 +52,20 @@ export interface ITextEditorModel extends IEditorModel { createSnapshot(this: IResolvedTextEditorModel): ITextSnapshot; createSnapshot(this: ITextEditorModel): ITextSnapshot | null; + /** + * Signals if this model is readonly or not. + */ isReadonly(): boolean; + + /** + * 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/resourceConfiguration.ts b/src/vs/editor/common/services/resourceConfiguration.ts deleted file mode 100644 index 0e122347e34f8..0000000000000 --- a/src/vs/editor/common/services/resourceConfiguration.ts +++ /dev/null @@ -1,47 +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 { URI } from 'vs/base/common/uri'; -import { IPosition } from 'vs/editor/common/core/position'; -import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const ITextResourceConfigurationService = createDecorator('textResourceConfigurationService'); - -export interface ITextResourceConfigurationService { - - _serviceBrand: undefined; - - /** - * Event that fires when the configuration changes. - */ - onDidChangeConfiguration: Event; - - /** - * Fetches the value of the section for the given resource by applying language overrides. - * Value can be of native type or an object keyed off the section name. - * - * @param resource - Resource for which the configuration has to be fetched. - * @param position - Position in the resource for which configuration has to be fetched. - * @param section - Section of the configuraion. - * - */ - getValue(resource: URI | undefined, section?: string): T; - getValue(resource: URI | undefined, position?: IPosition, section?: string): T; - -} - -export const ITextResourcePropertiesService = createDecorator('textResourcePropertiesService'); - -export interface ITextResourcePropertiesService { - - _serviceBrand: undefined; - - /** - * Returns the End of Line characters for the given resource - */ - getEOL(resource: URI | undefined, language?: string): string; -} \ No newline at end of file diff --git a/src/vs/editor/common/services/resourceConfigurationImpl.ts b/src/vs/editor/common/services/resourceConfigurationImpl.ts deleted file mode 100644 index 08983dc17b4fc..0000000000000 --- a/src/vs/editor/common/services/resourceConfigurationImpl.ts +++ /dev/null @@ -1,56 +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 { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import { IPosition, Position } from 'vs/editor/common/core/position'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; -import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; - -export class TextResourceConfigurationService extends Disposable implements ITextResourceConfigurationService { - - public _serviceBrand: undefined; - - private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); - public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; - - constructor( - @IConfigurationService private readonly configurationService: IConfigurationService, - @IModelService private readonly modelService: IModelService, - @IModeService private readonly modeService: IModeService, - ) { - super(); - this._register(this.configurationService.onDidChangeConfiguration(e => this._onDidChangeConfiguration.fire(e))); - } - - getValue(resource: URI, section?: string): T; - getValue(resource: URI, at?: IPosition, section?: string): T; - getValue(resource: URI, arg2?: any, arg3?: any): T { - if (typeof arg3 === 'string') { - return this._getValue(resource, Position.isIPosition(arg2) ? arg2 : null, arg3); - } - return this._getValue(resource, null, typeof arg2 === 'string' ? arg2 : undefined); - } - - private _getValue(resource: URI, 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 }); - } - return this.configurationService.getValue(section, { resource, overrideIdentifier: language }); - } - - private getLanguage(resource: URI, position: IPosition | null): string | null { - const model = this.modelService.getModel(resource); - if (model) { - return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column))!.language : model.getLanguageIdentifier().language; - } - return this.modeService.getModeIdByFilepathOrFirstLine(resource); - - } -} \ No newline at end of file diff --git a/src/vs/editor/common/services/semanticTokensProviderStyling.ts b/src/vs/editor/common/services/semanticTokensProviderStyling.ts new file mode 100644 index 0000000000000..e29d8f86e810f --- /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 new file mode 100644 index 0000000000000..23a2335b6dfd4 --- /dev/null +++ b/src/vs/editor/common/services/textResourceConfigurationService.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { IPosition } from 'vs/editor/common/core/position'; +import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ITextResourceConfigurationService = createDecorator('textResourceConfigurationService'); + +export interface ITextResourceConfigurationChangeEvent { + + /** + * All affected keys. Also includes language overrides and keys changed under language overrides. + */ + readonly affectedKeys: string[]; + + /** + * Returns `true` if the given section has changed for the given resource. + * + * Example: To check if the configuration section has changed for a given resource use `e.affectsConfiguration(resource, section)`. + * + * @param resource Resource for which the configuration has to be checked. + * @param section Section of the configuration + */ + affectsConfiguration(resource: URI, section: string): boolean; +} + +export interface ITextResourceConfigurationService { + + readonly _serviceBrand: undefined; + + /** + * Event that fires when the configuration changes. + */ + onDidChangeConfiguration: Event; + + /** + * Fetches the value of the section for the given resource by applying language overrides. + * Value can be of native type or an object keyed off the section name. + * + * @param resource - Resource for which the configuration has to be fetched. + * @param position - Position in the resource for which configuration has to be fetched. + * @param section - Section of the configuraion. + * + */ + getValue(resource: URI | undefined, section?: string): T; + getValue(resource: URI | undefined, position?: IPosition, section?: string): T; + + /** + * Update the configuration value for the given resource at the effective location. + * + * - If configurationTarget is not specified, target will be derived by checking where the configuration is defined. + * - If the language overrides for the give resource contains the configuration, then it is updated. + * + * @param resource Resource for which the configuration has to be updated + * @param key Configuration key + * @param value Configuration value + * @param configurationTarget Optional target into which the configuration has to be updated. + * If not specified, target will be derived by checking where the configuration is defined. + */ + updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise; + +} + +export const ITextResourcePropertiesService = createDecorator('textResourcePropertiesService'); + +export interface ITextResourcePropertiesService { + + readonly _serviceBrand: undefined; + + /** + * Returns the End of Line characters for the given resource + */ + getEOL(resource: URI, language?: string): string; +} diff --git a/src/vs/editor/common/services/textResourceConfigurationServiceImpl.ts b/src/vs/editor/common/services/textResourceConfigurationServiceImpl.ts new file mode 100644 index 0000000000000..7165061e0a677 --- /dev/null +++ b/src/vs/editor/common/services/textResourceConfigurationServiceImpl.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 { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ITextResourceConfigurationService, ITextResourceConfigurationChangeEvent } from 'vs/editor/common/services/textResourceConfigurationService'; +import { IConfigurationService, ConfigurationTarget, IConfigurationValue, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; + +export class TextResourceConfigurationService extends Disposable implements ITextResourceConfigurationService { + + public _serviceBrand: undefined; + + private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService, + ) { + super(); + this._register(this.configurationService.onDidChangeConfiguration(e => this._onDidChangeConfiguration.fire(this.toResourceConfigurationChangeEvent(e)))); + } + + 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); + } + return this._getValue(resource, null, typeof arg2 === 'string' ? arg2 : undefined); + } + + updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise { + const language = this.getLanguage(resource, null); + const configurationValue = this.configurationService.inspect(key, { resource, overrideIdentifier: language }); + if (configurationTarget === undefined) { + configurationTarget = this.deriveConfigurationTarget(configurationValue, language); + } + switch (configurationTarget) { + case ConfigurationTarget.MEMORY: + return this._updateValue(key, value, configurationTarget, configurationValue.memory?.override, resource, language); + case ConfigurationTarget.WORKSPACE_FOLDER: + return this._updateValue(key, value, configurationTarget, configurationValue.workspaceFolder?.override, resource, language); + case ConfigurationTarget.WORKSPACE: + return this._updateValue(key, value, configurationTarget, configurationValue.workspace?.override, resource, language); + case ConfigurationTarget.USER_REMOTE: + return this._updateValue(key, value, configurationTarget, configurationValue.userRemote?.override, resource, language); + default: + return this._updateValue(key, value, configurationTarget, configurationValue.userLocal?.override, resource, language); + } + } + + private _updateValue(key: string, value: any, configurationTarget: ConfigurationTarget, overriddenValue: any | undefined, resource: URI, language: string | null): Promise { + if (language && overriddenValue !== undefined) { + return this.configurationService.updateValue(key, value, { resource, overrideIdentifier: language }, configurationTarget); + } else { + return this.configurationService.updateValue(key, value, { resource }, configurationTarget); + } + } + + private deriveConfigurationTarget(configurationValue: IConfigurationValue, language: string | null): ConfigurationTarget { + if (language) { + if (configurationValue.memory?.override !== undefined) { + return ConfigurationTarget.MEMORY; + } + if (configurationValue.workspaceFolder?.override !== undefined) { + return ConfigurationTarget.WORKSPACE_FOLDER; + } + if (configurationValue.workspace?.override !== undefined) { + return ConfigurationTarget.WORKSPACE; + } + if (configurationValue.userRemote?.override !== undefined) { + return ConfigurationTarget.USER_REMOTE; + } + if (configurationValue.userLocal?.override !== undefined) { + return ConfigurationTarget.USER_LOCAL; + } + } + if (configurationValue.memory?.value !== undefined) { + return ConfigurationTarget.MEMORY; + } + if (configurationValue.workspaceFolder?.value !== undefined) { + return ConfigurationTarget.WORKSPACE_FOLDER; + } + if (configurationValue.workspace?.value !== undefined) { + return ConfigurationTarget.WORKSPACE; + } + if (configurationValue.userRemote?.value !== undefined) { + return ConfigurationTarget.USER_REMOTE; + } + return ConfigurationTarget.USER_LOCAL; + } + + 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 }); + } + return this.configurationService.getValue(section, { resource, overrideIdentifier: language }); + } + + private getLanguage(resource: URI, position: IPosition | null): string | null { + const model = this.modelService.getModel(resource); + if (model) { + return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column))!.language : model.getLanguageIdentifier().language; + } + return this.modeService.getModeIdByFilepathOrFirstLine(resource); + } + + private toResourceConfigurationChangeEvent(configurationChangeEvent: IConfigurationChangeEvent): ITextResourceConfigurationChangeEvent { + return { + affectedKeys: configurationChangeEvent.affectedKeys, + affectsConfiguration: (resource: URI, configuration: string) => { + const overrideIdentifier = this.getLanguage(resource, null); + return configurationChangeEvent.affectsConfiguration(configuration, { resource, overrideIdentifier }); + } + }; + } +} diff --git a/src/vs/editor/common/services/webWorker.ts b/src/vs/editor/common/services/webWorker.ts index 0ae55dffaa075..55dfe84af398c 100644 --- a/src/vs/editor/common/services/webWorker.ts +++ b/src/vs/editor/common/services/webWorker.ts @@ -53,6 +53,11 @@ export interface IWebWorkerOptions { * An object that can be used by the web worker to make calls back to the main thread. */ host?: any; + /** + * Keep idle models. + * Defaults to false, which means that idle models will stop syncing after a while. + */ + keepIdleModels?: boolean; } class MonacoWebWorkerImpl extends EditorWorkerClient implements MonacoWebWorker { @@ -63,7 +68,7 @@ class MonacoWebWorkerImpl extends EditorWorkerClient implements MonacoWebWork private _foreignProxy: Promise | null; constructor(modelService: IModelService, opts: IWebWorkerOptions) { - super(modelService, opts.label); + super(modelService, opts.keepIdleModels || false, opts.label); this._foreignModuleId = opts.moduleId; this._foreignModuleCreateData = opts.createData || null; this._foreignModuleHost = opts.host || null; 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 b62e25bccff3c..0000000000000 --- 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 4ddfcab7cd051..0000000000000 --- 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/promise-polyfill/polyfill.license.txt b/src/vs/editor/common/standalone/promise-polyfill/polyfill.license.txt deleted file mode 100644 index 6f7c012316280..0000000000000 --- a/src/vs/editor/common/standalone/promise-polyfill/polyfill.license.txt +++ /dev/null @@ -1,20 +0,0 @@ -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. diff --git a/src/vs/editor/common/standalone/standaloneBase.ts b/src/vs/editor/common/standalone/standaloneBase.ts index 377b5185c2829..2239e8d0234a2 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 c26b7705b5ca6..b1832df3859af 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -6,16 +6,341 @@ // THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY. -export enum MarkerTag { - Unnecessary = 1, - Deprecated = 2 +export enum AccessibilitySupport { + /** + * This should be the browser case where it is not known if a screen reader is attached or no. + */ + Unknown = 0, + Disabled = 1, + Enabled = 2 } -export enum MarkerSeverity { - Hint = 1, - Info = 2, - Warning = 4, - Error = 8 +export enum CompletionItemInsertTextRule { + /** + * Adjust whitespace/indentation of multiline insert texts to + * match the current line indentation. + */ + KeepWhitespace = 1, + /** + * `insertText` is a snippet. + */ + InsertAsSnippet = 4 +} + +export enum CompletionItemKind { + Method = 0, + Function = 1, + Constructor = 2, + Field = 3, + Variable = 4, + Class = 5, + Struct = 6, + Interface = 7, + Module = 8, + Property = 9, + Event = 10, + Operator = 11, + Unit = 12, + Value = 13, + Constant = 14, + Enum = 15, + EnumMember = 16, + Keyword = 17, + Text = 18, + Color = 19, + File = 20, + Reference = 21, + Customcolor = 22, + Folder = 23, + TypeParameter = 24, + User = 25, + Issue = 26, + Snippet = 27 +} + +export enum CompletionItemTag { + Deprecated = 1 +} + +/** + * How a suggest provider was triggered. + */ +export enum CompletionTriggerKind { + Invoke = 0, + TriggerCharacter = 1, + TriggerForIncompleteCompletions = 2 +} + +/** + * A positioning preference for rendering content widgets. + */ +export enum ContentWidgetPositionPreference { + /** + * Place the content widget exactly at a position + */ + EXACT = 0, + /** + * Place the content widget above a position + */ + ABOVE = 1, + /** + * Place the content widget below a position + */ + BELOW = 2 +} + +/** + * Describes the reason the cursor has changed its position. + */ +export enum CursorChangeReason { + /** + * Unknown or not set. + */ + NotSet = 0, + /** + * A `model.setValue()` was called. + */ + ContentFlush = 1, + /** + * The `model` has been changed outside of this cursor and the cursor recovers its position from associated markers. + */ + RecoverFromMarkers = 2, + /** + * There was an explicit user gesture. + */ + Explicit = 3, + /** + * There was a Paste. + */ + Paste = 4, + /** + * There was an Undo. + */ + Undo = 5, + /** + * There was a Redo. + */ + Redo = 6 +} + +/** + * The default end of line to use when instantiating models. + */ +export enum DefaultEndOfLine { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * A document highlight kind. + */ +export enum DocumentHighlightKind { + /** + * A textual occurrence. + */ + Text = 0, + /** + * Read-access of a symbol, like reading a variable. + */ + Read = 1, + /** + * Write-access of a symbol, like writing to a variable. + */ + Write = 2 +} + +/** + * Configuration options for auto indentation in the editor + */ +export enum EditorAutoIndentStrategy { + None = 0, + Keep = 1, + Brackets = 2, + Advanced = 3, + Full = 4 +} + +export enum EditorOption { + acceptSuggestionOnCommitCharacter = 0, + acceptSuggestionOnEnter = 1, + accessibilitySupport = 2, + accessibilityPageSize = 3, + ariaLabel = 4, + autoClosingBrackets = 5, + autoClosingOvertype = 6, + autoClosingQuotes = 7, + autoIndent = 8, + automaticLayout = 9, + autoSurround = 10, + codeLens = 11, + colorDecorators = 12, + 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, + smoothScrolling = 94, + stopRenderingLineAfter = 95, + suggest = 96, + suggestFontSize = 97, + suggestLineHeight = 98, + suggestOnTriggerCharacters = 99, + suggestSelection = 100, + tabCompletion = 101, + unusualLineTerminators = 102, + useTabStops = 103, + wordSeparators = 104, + wordWrap = 105, + wordWrapBreakAfterCharacters = 106, + wordWrapBreakBeforeCharacters = 107, + wordWrapColumn = 108, + wordWrapMinified = 109, + wrappingIndent = 110, + wrappingStrategy = 111, + showDeprecated = 112, + editorClassName = 113, + pixelRatio = 114, + tabFocusMode = 115, + layoutInfo = 116, + wrappingInfo = 117 +} + +/** + * End of line character preference. + */ +export enum EndOfLinePreference { + /** + * Use the end of line character identified in the text buffer. + */ + TextDefined = 0, + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * End of line character preference. + */ +export enum EndOfLineSequence { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 0, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 1 +} + +/** + * Describes what to do with the indentation when pressing Enter. + */ +export enum IndentAction { + /** + * Insert new line and copy the previous line's indentation. + */ + None = 0, + /** + * Insert new line and indent once (relative to the previous line's indentation). + */ + Indent = 1, + /** + * Insert two new lines: + * - the first one indented which will hold the cursor + * - the second one at the same indentation level + */ + IndentOutdent = 2, + /** + * Insert new line and outdent once (relative to the previous line's indentation). + */ + Outdent = 3 } /** @@ -199,34 +524,16 @@ export enum KeyCode { MAX_VALUE = 112 } -/** - * The direction of a selection. - */ -export enum SelectionDirection { - /** - * The selection starts above where it ends. - */ - LTR = 0, - /** - * The selection starts below where it ends. - */ - RTL = 1 -} - -export enum ScrollbarVisibility { - Auto = 1, - Hidden = 2, - Visible = 3 +export enum MarkerSeverity { + Hint = 1, + Info = 2, + Warning = 4, + Error = 8 } -/** - * Vertical Lane in the overview ruler of the editor. - */ -export enum OverviewRulerLane { - Left = 1, - Center = 2, - Right = 4, - Full = 7 +export enum MarkerTag { + Unnecessary = 1, + Deprecated = 2 } /** @@ -238,60 +545,107 @@ export enum MinimapPosition { } /** - * End of line character preference. + * Type of hit element with the mouse in the editor. */ -export enum EndOfLinePreference { +export enum MouseTargetType { /** - * Use the end of line character identified in the text buffer. + * Mouse is on top of an unknown element. */ - TextDefined = 0, + UNKNOWN = 0, /** - * Use line feed (\n) as the end of line character. + * Mouse is on top of the textarea used for input. */ - LF = 1, + TEXTAREA = 1, /** - * Use carriage return and line feed (\r\n) as the end of line character. + * Mouse is on top of the glyph margin */ - CRLF = 2 -} - -/** - * The default end of line to use when instantiating models. - */ -export enum DefaultEndOfLine { + GUTTER_GLYPH_MARGIN = 2, /** - * Use line feed (\n) as the end of line character. + * Mouse is on top of the line numbers */ - LF = 1, + GUTTER_LINE_NUMBERS = 3, /** - * Use carriage return and line feed (\r\n) as the end of line character. + * Mouse is on top of the line decorations */ - CRLF = 2 + GUTTER_LINE_DECORATIONS = 4, + /** + * Mouse is on top of the whitespace left in the gutter by a view zone. + */ + GUTTER_VIEW_ZONE = 5, + /** + * Mouse is on top of text in the content. + */ + CONTENT_TEXT = 6, + /** + * Mouse is on top of empty space in the content (e.g. after line text or below last line) + */ + CONTENT_EMPTY = 7, + /** + * Mouse is on top of a view zone in the content. + */ + CONTENT_VIEW_ZONE = 8, + /** + * Mouse is on top of a content widget. + */ + CONTENT_WIDGET = 9, + /** + * Mouse is on top of the decorations overview ruler. + */ + OVERVIEW_RULER = 10, + /** + * Mouse is on top of a scrollbar. + */ + SCROLLBAR = 11, + /** + * Mouse is on top of an overlay widget. + */ + OVERLAY_WIDGET = 12, + /** + * Mouse is outside of the editor. + */ + OUTSIDE_EDITOR = 13 } /** - * End of line character preference. + * A positioning preference for rendering overlay widgets. */ -export enum EndOfLineSequence { +export enum OverlayWidgetPositionPreference { /** - * Use line feed (\n) as the end of line character. + * Position the overlay widget in the top right corner */ - LF = 0, + TOP_RIGHT_CORNER = 0, /** - * Use carriage return and line feed (\r\n) as the end of line character. + * Position the overlay widget in the bottom right corner */ - CRLF = 1 + BOTTOM_RIGHT_CORNER = 1, + /** + * Position the overlay widget in the top center + */ + TOP_CENTER = 2 } /** - * Describes the behavior of decorations when typing/editing near their edges. - * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` + * Vertical Lane in the overview ruler of the editor. */ -export enum TrackedRangeStickiness { - AlwaysGrowsWhenTypingAtEdges = 0, - NeverGrowsWhenTypingAtEdges = 1, - GrowsOnlyWhenTypingBefore = 2, - GrowsOnlyWhenTypingAfter = 3 +export enum OverviewRulerLane { + Left = 1, + Center = 2, + Right = 4, + Full = 7 +} + +export enum RenderLineNumbersType { + Off = 0, + On = 1, + Relative = 2, + Interval = 3, + Custom = 4 +} + +export enum RenderMinimap { + None = 0, + Text = 1, + Blocks = 2 } export enum ScrollType { @@ -299,47 +653,66 @@ export enum ScrollType { Immediate = 1 } +export enum ScrollbarVisibility { + Auto = 1, + Hidden = 2, + Visible = 3 +} + /** - * Describes the reason the cursor has changed its position. + * The direction of a selection. */ -export enum CursorChangeReason { - /** - * Unknown or not set. - */ - NotSet = 0, - /** - * A `model.setValue()` was called. - */ - ContentFlush = 1, - /** - * The `model` has been changed outside of this cursor and the cursor recovers its position from associated markers. - */ - RecoverFromMarkers = 2, - /** - * There was an explicit user gesture. - */ - Explicit = 3, - /** - * There was a Paste. - */ - Paste = 4, +export enum SelectionDirection { /** - * There was an Undo. + * The selection starts above where it ends. */ - Undo = 5, + LTR = 0, /** - * There was a Redo. + * The selection starts below where it ends. */ - Redo = 6 + RTL = 1 } -export enum AccessibilitySupport { - /** - * This should be the browser case where it is not known if a screen reader is attached or no. - */ - Unknown = 0, - Disabled = 1, - Enabled = 2 +export enum SignatureHelpTriggerKind { + Invoke = 1, + TriggerCharacter = 2, + ContentChange = 3 +} + +/** + * A symbol kind. + */ +export enum SymbolKind { + File = 0, + Module = 1, + Namespace = 2, + Package = 3, + Class = 4, + Method = 5, + Property = 6, + Field = 7, + Constructor = 8, + Enum = 9, + Interface = 10, + Function = 11, + Variable = 12, + Constant = 13, + String = 14, + Number = 15, + Boolean = 16, + Array = 17, + Object = 18, + Key = 19, + Null = 20, + EnumMember = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25 +} + +export enum SymbolTag { + Deprecated = 1 } /** @@ -402,18 +775,15 @@ export enum TextEditorCursorStyle { UnderlineThin = 6 } -export enum RenderMinimap { - None = 0, - Text = 1, - Blocks = 2 -} - -export enum RenderLineNumbersType { - Off = 0, - On = 1, - Relative = 2, - Interval = 3, - Custom = 4 +/** + * Describes the behavior of decorations when typing/editing near their edges. + * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` + */ +export enum TrackedRangeStickiness { + AlwaysGrowsWhenTypingAtEdges = 0, + NeverGrowsWhenTypingAtEdges = 1, + GrowsOnlyWhenTypingBefore = 2, + GrowsOnlyWhenTypingAfter = 3 } /** @@ -436,240 +806,4 @@ export enum WrappingIndent { * DeepIndent => wrapped lines get +2 indentation toward the parent. */ DeepIndent = 3 -} - -/** - * A positioning preference for rendering content widgets. - */ -export enum ContentWidgetPositionPreference { - /** - * Place the content widget exactly at a position - */ - EXACT = 0, - /** - * Place the content widget above a position - */ - ABOVE = 1, - /** - * Place the content widget below a position - */ - BELOW = 2 -} - -/** - * A positioning preference for rendering overlay widgets. - */ -export enum OverlayWidgetPositionPreference { - /** - * Position the overlay widget in the top right corner - */ - TOP_RIGHT_CORNER = 0, - /** - * Position the overlay widget in the bottom right corner - */ - BOTTOM_RIGHT_CORNER = 1, - /** - * Position the overlay widget in the top center - */ - TOP_CENTER = 2 -} - -/** - * Type of hit element with the mouse in the editor. - */ -export enum MouseTargetType { - /** - * Mouse is on top of an unknown element. - */ - UNKNOWN = 0, - /** - * Mouse is on top of the textarea used for input. - */ - TEXTAREA = 1, - /** - * Mouse is on top of the glyph margin - */ - GUTTER_GLYPH_MARGIN = 2, - /** - * Mouse is on top of the line numbers - */ - GUTTER_LINE_NUMBERS = 3, - /** - * Mouse is on top of the line decorations - */ - GUTTER_LINE_DECORATIONS = 4, - /** - * Mouse is on top of the whitespace left in the gutter by a view zone. - */ - GUTTER_VIEW_ZONE = 5, - /** - * Mouse is on top of text in the content. - */ - CONTENT_TEXT = 6, - /** - * Mouse is on top of empty space in the content (e.g. after line text or below last line) - */ - CONTENT_EMPTY = 7, - /** - * Mouse is on top of a view zone in the content. - */ - CONTENT_VIEW_ZONE = 8, - /** - * Mouse is on top of a content widget. - */ - CONTENT_WIDGET = 9, - /** - * Mouse is on top of the decorations overview ruler. - */ - OVERVIEW_RULER = 10, - /** - * Mouse is on top of a scrollbar. - */ - SCROLLBAR = 11, - /** - * Mouse is on top of an overlay widget. - */ - OVERLAY_WIDGET = 12, - /** - * Mouse is outside of the editor. - */ - OUTSIDE_EDITOR = 13 -} - -/** - * Describes what to do with the indentation when pressing Enter. - */ -export enum IndentAction { - /** - * Insert new line and copy the previous line's indentation. - */ - None = 0, - /** - * Insert new line and indent once (relative to the previous line's indentation). - */ - Indent = 1, - /** - * Insert two new lines: - * - the first one indented which will hold the cursor - * - the second one at the same indentation level - */ - IndentOutdent = 2, - /** - * Insert new line and outdent once (relative to the previous line's indentation). - */ - Outdent = 3 -} - -export enum CompletionItemKind { - Method = 0, - Function = 1, - Constructor = 2, - Field = 3, - Variable = 4, - Class = 5, - Struct = 6, - Interface = 7, - Module = 8, - Property = 9, - Event = 10, - Operator = 11, - Unit = 12, - Value = 13, - Constant = 14, - Enum = 15, - EnumMember = 16, - Keyword = 17, - Text = 18, - Color = 19, - File = 20, - Reference = 21, - Customcolor = 22, - Folder = 23, - TypeParameter = 24, - Snippet = 25 -} - -export enum CompletionItemTag { - Deprecated = 1 -} - -export enum CompletionItemInsertTextRule { - /** - * Adjust whitespace/indentation of multiline insert texts to - * match the current line indentation. - */ - KeepWhitespace = 1, - /** - * `insertText` is a snippet. - */ - InsertAsSnippet = 4 -} - -/** - * How a suggest provider was triggered. - */ -export enum CompletionTriggerKind { - Invoke = 0, - TriggerCharacter = 1, - TriggerForIncompleteCompletions = 2 -} - -export enum SignatureHelpTriggerKind { - Invoke = 1, - TriggerCharacter = 2, - ContentChange = 3 -} - -/** - * A document highlight kind. - */ -export enum DocumentHighlightKind { - /** - * A textual occurrence. - */ - Text = 0, - /** - * Read-access of a symbol, like reading a variable. - */ - Read = 1, - /** - * Write-access of a symbol, like writing to a variable. - */ - Write = 2 -} - -/** - * A symbol kind. - */ -export enum SymbolKind { - File = 0, - Module = 1, - Namespace = 2, - Package = 3, - Class = 4, - Method = 5, - Property = 6, - Field = 7, - Constructor = 8, - Enum = 9, - Interface = 10, - Function = 11, - Variable = 12, - Constant = 13, - String = 14, - Number = 15, - Boolean = 16, - Array = 17, - Object = 18, - Key = 19, - Null = 20, - EnumMember = 21, - Struct = 22, - Event = 23, - Operator = 24, - TypeParameter = 25 -} - -export enum SymbolTag { - Deprecated = 1 } \ No newline at end of file diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index f0c107c0cbe02..88104a63373eb 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 23d5e072b9dba..89c3c90dc59f6 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { Color, RGBA } from 'vs/base/common/color'; -import { activeContrastBorder, editorBackground, editorForeground, registerColor, editorWarningForeground, editorInfoForeground, editorWarningBorder, editorInfoBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { activeContrastBorder, editorBackground, editorForeground, registerColor, editorWarningForeground, editorInfoForeground, editorWarningBorder, editorInfoBorder, contrastBorder, editorFindMatchHighlight } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; /** @@ -15,6 +15,8 @@ export const editorLineHighlight = registerColor('editor.lineHighlightBackground export const editorLineHighlightBorder = registerColor('editor.lineHighlightBorder', { dark: '#282828', light: '#eeeeee', hc: '#f38518' }, nls.localize('lineHighlightBorderBox', 'Background color for the border around the line at the cursor position.')); export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hc: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorRangeHighlightBorder = registerColor('editor.rangeHighlightBorder', { dark: null, light: null, hc: activeContrastBorder }, nls.localize('rangeHighlightBorder', 'Background color of the border around highlighted ranges.'), true); +export const editorSymbolHighlight = registerColor('editor.symbolHighlightBackground', { dark: editorFindMatchHighlight, light: editorFindMatchHighlight, hc: null }, nls.localize('symbolHighlight', 'Background color of highlighted symbol, like for go to definition or go next/previous symbol. The color must not be opaque so as not to hide underlying decorations.'), true); +export const editorSymbolHighlightBorder = registerColor('editor.symbolHighlightBorder', { dark: null, light: null, hc: activeContrastBorder }, nls.localize('symbolHighlightBorder', 'Background color of the border around highlighted symbols.'), true); export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hc: Color.white }, nls.localize('caret', 'Color of the editor cursor.')); export const editorCursorBackground = registerColor('editorCursor.background', null, nls.localize('editorCursorBackground', 'The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.')); @@ -28,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.')); @@ -73,8 +76,19 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .rangeHighlight { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${rangeHighlightBorder}; }`); } + const symbolHighlight = theme.getColor(editorSymbolHighlight); + if (symbolHighlight) { + collector.addRule(`.monaco-editor .symbolHighlight { background-color: ${symbolHighlight}; }`); + } + + const symbolHighlightBorder = theme.getColor(editorSymbolHighlightBorder); + if (symbolHighlightBorder) { + collector.addRule(`.monaco-editor .symbolHighlight { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${symbolHighlightBorder}; }`); + } + 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 5f4c9be5889ca..b888fbec6ad06 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 54bd7a6e9862b..0000000000000 --- 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 c82cf9ac36071..804d1b977f542 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -3,31 +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 } from 'vs/editor/common/editorCommon'; +import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; export const enum ViewEventType { - ViewConfigurationChanged = 1, - ViewCursorStateChanged = 2, - ViewDecorationsChanged = 3, - ViewFlushed = 4, - ViewFocusChanged = 5, - ViewLineMappingChanged = 6, - ViewLinesChanged = 7, - ViewLinesDeleted = 8, - ViewLinesInserted = 9, - ViewRevealRangeRequest = 10, - ViewScrollChanged = 11, - ViewTokensChanged = 12, - ViewTokensColorsChanged = 13, - ViewZonesChanged = 14, - ViewThemeChanged = 15, - ViewLanguageConfigurationChanged = 16 + ViewConfigurationChanged, + ViewCursorStateChanged, + ViewDecorationsChanged, + ViewFlushed, + ViewFocusChanged, + ViewLanguageConfigurationChanged, + ViewLineMappingChanged, + ViewLinesChanged, + ViewLinesDeleted, + ViewLinesInserted, + ViewRevealRangeRequest, + ViewScrollChanged, + ViewThemeChanged, + ViewTokensChanged, + ViewTokensColorsChanged, + ViewZonesChanged, } export class ViewConfigurationChangedEvent { @@ -49,13 +48,12 @@ export class ViewCursorStateChangedEvent { public readonly type = ViewEventType.ViewCursorStateChanged; - /** - * The primary selection is always at index 0. - */ public readonly selections: Selection[]; + public readonly modelSelections: Selection[]; - constructor(selections: Selection[]) { + constructor(selections: Selection[], modelSelections: Selection[]) { this.selections = selections; + this.modelSelections = modelSelections; } } @@ -63,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; + } } } @@ -88,6 +95,11 @@ export class ViewFocusChangedEvent { } } +export class ViewLanguageConfigurationEvent { + + public readonly type = ViewEventType.ViewLanguageConfigurationChanged; +} + export class ViewLineMappingChangedEvent { public readonly type = ViewEventType.ViewLineMappingChanged; @@ -159,7 +171,9 @@ export const enum VerticalRevealType { Center = 1, CenterIfOutsideViewport = 2, Top = 3, - Bottom = 4 + Bottom = 4, + NearTop = 5, + NearTopIfOutsideViewport = 6, } export class ViewRevealRangeRequestEvent { @@ -169,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; /** @@ -183,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; @@ -221,6 +241,11 @@ export class ViewScrollChangedEvent { } } +export class ViewThemeChangedEvent { + + public readonly type = ViewEventType.ViewThemeChanged; +} + export class ViewTokensChangedEvent { public readonly type = ViewEventType.ViewTokensChanged; @@ -241,11 +266,6 @@ export class ViewTokensChangedEvent { } } -export class ViewThemeChangedEvent { - - public readonly type = ViewEventType.ViewThemeChanged; -} - export class ViewTokensColorsChangedEvent { public readonly type = ViewEventType.ViewTokensColorsChanged; @@ -264,117 +284,21 @@ export class ViewZonesChangedEvent { } } -export class ViewLanguageConfigurationEvent { - - public readonly type = ViewEventType.ViewLanguageConfigurationChanged; -} - export type ViewEvent = ( ViewConfigurationChangedEvent | ViewCursorStateChangedEvent | ViewDecorationsChangedEvent | ViewFlushedEvent | ViewFocusChangedEvent - | ViewLinesChangedEvent + | ViewLanguageConfigurationEvent | ViewLineMappingChangedEvent + | ViewLinesChangedEvent | ViewLinesDeletedEvent | ViewLinesInsertedEvent | ViewRevealRangeRequestEvent | ViewScrollChangedEvent + | ViewThemeChangedEvent | ViewTokensChangedEvent | ViewTokensColorsChangedEvent | ViewZonesChangedEvent - | ViewThemeChangedEvent - | ViewLanguageConfigurationEvent ); - -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 367abd68f8257..74428ae28d786 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 d6673ac8b8fbe..ceef064de9abc 100644 --- a/src/vs/editor/common/viewLayout/linesLayout.ts +++ b/src/vs/editor/common/viewLayout/linesLayout.ts @@ -4,84 +4,327 @@ *--------------------------------------------------------------------------------------------*/ import { IPartialViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; -import { IEditorWhitespace, WhitespaceComputer } from 'vs/editor/common/viewLayout/whitespaceComputer'; import { IViewWhitespaceViewportData } from 'vs/editor/common/viewModel/viewModel'; +import * as strings from 'vs/base/common/strings'; + +export interface IEditorWhitespace { + readonly id: string; + readonly afterLineNumber: number; + readonly height: number; +} + +/** + * An accessor that allows for whtiespace to be added, removed or changed in bulk. + */ +export interface IWhitespaceChangeAccessor { + insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string; + changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void; + removeWhitespace(id: string): void; +} + +interface IPendingChange { id: string; newAfterLineNumber: number; newHeight: number; } +interface IPendingRemove { id: string; } + +class PendingChanges { + private _hasPending: boolean; + private _inserts: EditorWhitespace[]; + private _changes: IPendingChange[]; + private _removes: IPendingRemove[]; + + constructor() { + this._hasPending = false; + this._inserts = []; + this._changes = []; + this._removes = []; + } + + public insert(x: EditorWhitespace): void { + this._hasPending = true; + this._inserts.push(x); + } + + public change(x: IPendingChange): void { + this._hasPending = true; + this._changes.push(x); + } + + public remove(x: IPendingRemove): void { + this._hasPending = true; + this._removes.push(x); + } + + public mustCommit(): boolean { + return this._hasPending; + } + + public commit(linesLayout: LinesLayout): void { + if (!this._hasPending) { + return; + } + + const inserts = this._inserts; + const changes = this._changes; + const removes = this._removes; + + this._hasPending = false; + this._inserts = []; + this._changes = []; + this._removes = []; + + linesLayout._commitPendingChanges(inserts, changes, removes); + } +} + +export class EditorWhitespace implements IEditorWhitespace { + public id: string; + public afterLineNumber: number; + public ordinal: number; + public height: number; + public minWidth: number; + public prefixSum: number; + + constructor(id: string, afterLineNumber: number, ordinal: number, height: number, minWidth: number) { + this.id = id; + this.afterLineNumber = afterLineNumber; + this.ordinal = ordinal; + this.height = height; + this.minWidth = minWidth; + this.prefixSum = 0; + } +} /** * Layouting of objects that take vertical space (by having a height) and push down other objects. * * These objects are basically either text (lines) or spaces between those lines (whitespaces). * This provides commodity operations for working with lines that contain whitespace that pushes lines lower (vertically). - * This is written with no knowledge of an editor in mind. */ export class LinesLayout { - /** - * Keep track of the total number of lines. - * This is useful for doing binary searches or for doing hit-testing. - */ - private _lineCount: number; + private static INSTANCE_COUNT = 0; - /** - * The height of a line in pixels. - */ + private readonly _instanceId: string; + private readonly _pendingChanges: PendingChanges; + private _lastWhitespaceId: number; + private _arr: EditorWhitespace[]; + private _prefixSumValidIndex: number; + private _minWidth: number; + private _lineCount: number; private _lineHeight: number; + private _paddingTop: number; + private _paddingBottom: number; + + constructor(lineCount: number, lineHeight: number, paddingTop: number, paddingBottom: number) { + this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT); + this._pendingChanges = new PendingChanges(); + this._lastWhitespaceId = 0; + this._arr = []; + this._prefixSumValidIndex = -1; + this._minWidth = -1; /* marker for not being computed */ + this._lineCount = lineCount; + this._lineHeight = lineHeight; + this._paddingTop = paddingTop; + this._paddingBottom = paddingBottom; + } /** - * Contains whitespace information in pixels + * Find the insertion index for a new value inside a sorted array of values. + * If the value is already present in the sorted array, the insertion index will be after the already existing value. */ - private readonly _whitespaces: WhitespaceComputer; + public static findInsertionIndex(arr: EditorWhitespace[], afterLineNumber: number, ordinal: number): number { + let low = 0; + let high = arr.length; - constructor(lineCount: number, lineHeight: number) { - this._lineCount = lineCount; - this._lineHeight = lineHeight; - this._whitespaces = new WhitespaceComputer(); + while (low < high) { + const mid = ((low + high) >>> 1); + + if (afterLineNumber === arr[mid].afterLineNumber) { + if (ordinal < arr[mid].ordinal) { + high = mid; + } else { + low = mid + 1; + } + } else if (afterLineNumber < arr[mid].afterLineNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + return low; } /** * Change the height of a line in pixels. */ public setLineHeight(lineHeight: number): void { + this._checkPendingChanges(); 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. * * @param lineCount New number of lines. */ public onFlushed(lineCount: number): void { + this._checkPendingChanges(); this._lineCount = lineCount; } - /** - * Insert a new whitespace of a certain height after a line number. - * The whitespace has a "sticky" characteristic. - * Irrespective of edits above or below `afterLineNumber`, the whitespace will follow the initial line. - * - * @param afterLineNumber The conceptual position of this whitespace. The whitespace will follow this line as best as possible even when deleting/inserting lines above/below. - * @param heightInPx The height of the whitespace, in pixels. - * @return An id that can be used later to mutate or delete the whitespace - */ - public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { - return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth); + public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean { + let hadAChange = false; + try { + 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 }); + } + }; + callback(accessor); + } finally { + this._pendingChanges.commit(this); + } + return hadAChange; } - /** - * Change properties associated with a certain whitespace. - */ - public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { - return this._whitespaces.changeWhitespace(id, newAfterLineNumber, newHeight); + public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void { + if (inserts.length > 0 || removes.length > 0) { + this._minWidth = -1; /* marker for not being computed */ + } + + if (inserts.length + changes.length + removes.length <= 1) { + // when only one thing happened, handle it "delicately" + for (const insert of inserts) { + this._insertWhitespace(insert); + } + for (const change of changes) { + this._changeOneWhitespace(change.id, change.newAfterLineNumber, change.newHeight); + } + for (const remove of removes) { + const index = this._findWhitespaceIndex(remove.id); + if (index === -1) { + continue; + } + this._removeWhitespace(index); + } + return; + } + + // simply rebuild the entire datastructure + + const toRemove = new Set(); + for (const remove of removes) { + toRemove.add(remove.id); + } + + const toChange = new Map(); + for (const change of changes) { + toChange.set(change.id, change); + } + + const applyRemoveAndChange = (whitespaces: EditorWhitespace[]): EditorWhitespace[] => { + let result: EditorWhitespace[] = []; + for (const whitespace of whitespaces) { + if (toRemove.has(whitespace.id)) { + continue; + } + if (toChange.has(whitespace.id)) { + const change = toChange.get(whitespace.id)!; + whitespace.afterLineNumber = change.newAfterLineNumber; + whitespace.height = change.newHeight; + } + result.push(whitespace); + } + return result; + }; + + const result = applyRemoveAndChange(this._arr).concat(applyRemoveAndChange(inserts)); + result.sort((a, b) => { + if (a.afterLineNumber === b.afterLineNumber) { + return a.ordinal - b.ordinal; + } + return a.afterLineNumber - b.afterLineNumber; + }); + + this._arr = result; + this._prefixSumValidIndex = -1; } - /** - * Remove an existing whitespace. - * - * @param id The whitespace to remove - * @return Returns true if the whitespace is found and it is removed. - */ - public removeWhitespace(id: string): boolean { - return this._whitespaces.removeWhitespace(id); + private _checkPendingChanges(): void { + if (this._pendingChanges.mustCommit()) { + this._pendingChanges.commit(this); + } + } + + private _insertWhitespace(whitespace: EditorWhitespace): void { + const insertIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); + this._arr.splice(insertIndex, 0, whitespace); + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); + } + + private _findWhitespaceIndex(id: string): number { + const arr = this._arr; + for (let i = 0, len = arr.length; i < len; i++) { + if (arr[i].id === id) { + return i; + } + } + return -1; + } + + private _changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void { + const index = this._findWhitespaceIndex(id); + if (index === -1) { + return; + } + if (this._arr[index].height !== newHeight) { + this._arr[index].height = newHeight; + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); + } + if (this._arr[index].afterLineNumber !== newAfterLineNumber) { + // `afterLineNumber` changed for this whitespace + + // Record old whitespace + const whitespace = this._arr[index]; + + // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace + this._removeWhitespace(index); + + whitespace.afterLineNumber = newAfterLineNumber; + + // And add it again + this._insertWhitespace(whitespace); + } + } + + private _removeWhitespace(removeIndex: number): void { + this._arr.splice(removeIndex, 1); + this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); } /** @@ -91,8 +334,24 @@ export class LinesLayout { * @param toLineNumber The line number at which the deletion ended, inclusive */ public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void { + this._checkPendingChanges(); + fromLineNumber = fromLineNumber | 0; + toLineNumber = toLineNumber | 0; + this._lineCount -= (toLineNumber - fromLineNumber + 1); - this._whitespaces.onLinesDeleted(fromLineNumber, toLineNumber); + for (let i = 0, len = this._arr.length; i < len; i++) { + const afterLineNumber = this._arr[i].afterLineNumber; + + if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) { + // The line this whitespace was after has been deleted + // => move whitespace to before first deleted line + this._arr[i].afterLineNumber = fromLineNumber - 1; + } else if (afterLineNumber > toLineNumber) { + // The line this whitespace was after has been moved up + // => move whitespace up + this._arr[i].afterLineNumber -= (toLineNumber - fromLineNumber + 1); + } + } } /** @@ -102,8 +361,53 @@ export class LinesLayout { * @param toLineNumber The line number at which the insertion ended, inclusive. */ public onLinesInserted(fromLineNumber: number, toLineNumber: number): void { + this._checkPendingChanges(); + fromLineNumber = fromLineNumber | 0; + toLineNumber = toLineNumber | 0; + this._lineCount += (toLineNumber - fromLineNumber + 1); - this._whitespaces.onLinesInserted(fromLineNumber, toLineNumber); + for (let i = 0, len = this._arr.length; i < len; i++) { + const afterLineNumber = this._arr[i].afterLineNumber; + + if (fromLineNumber <= afterLineNumber) { + this._arr[i].afterLineNumber += (toLineNumber - fromLineNumber + 1); + } + } + } + + /** + * Get the sum of all the whitespaces. + */ + public getWhitespacesTotalHeight(): number { + this._checkPendingChanges(); + if (this._arr.length === 0) { + return 0; + } + return this.getWhitespacesAccumulatedHeight(this._arr.length - 1); + } + + /** + * Return the sum of the heights of the whitespaces at [0..index]. + * This includes the whitespace at `index`. + * + * @param index The index of the whitespace. + * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`. + */ + public getWhitespacesAccumulatedHeight(index: number): number { + this._checkPendingChanges(); + index = index | 0; + + let startIndex = Math.max(0, this._prefixSumValidIndex + 1); + if (startIndex === 0) { + this._arr[0].prefixSum = this._arr[0].height; + startIndex++; + } + + for (let i = startIndex; i <= index; i++) { + this._arr[i].prefixSum = this._arr[i - 1].prefixSum + this._arr[i].height; + } + this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index); + return this._arr[index].prefixSum; } /** @@ -112,9 +416,80 @@ export class LinesLayout { * @return The sum of heights for all objects. */ public getLinesTotalHeight(): number { - let linesHeight = this._lineHeight * this._lineCount; - let whitespacesHeight = this._whitespaces.getTotalHeight(); - return linesHeight + whitespacesHeight; + this._checkPendingChanges(); + const linesHeight = this._lineHeight * this._lineCount; + const whitespacesHeight = this.getWhitespacesTotalHeight(); + + return linesHeight + whitespacesHeight + this._paddingTop + this._paddingBottom; + } + + /** + * Returns the accumulated height of whitespaces before the given line number. + * + * @param lineNumber The line number + */ + public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number { + this._checkPendingChanges(); + lineNumber = lineNumber | 0; + + const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); + + if (lastWhitespaceBeforeLineNumber === -1) { + return 0; + } + + return this.getWhitespacesAccumulatedHeight(lastWhitespaceBeforeLineNumber); + } + + private _findLastWhitespaceBeforeLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + // Find the whitespace before line number + const arr = this._arr; + let low = 0; + let high = arr.length - 1; + + while (low <= high) { + const delta = (high - low) | 0; + const halfDelta = (delta / 2) | 0; + const mid = (low + halfDelta) | 0; + + if (arr[mid].afterLineNumber < lineNumber) { + if (mid + 1 >= arr.length || arr[mid + 1].afterLineNumber >= lineNumber) { + return mid; + } else { + low = (mid + 1) | 0; + } + } else { + high = (mid - 1) | 0; + } + } + + return -1; + } + + private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number { + lineNumber = lineNumber | 0; + + const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); + const firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1; + + if (firstWhitespaceAfterLineNumber < this._arr.length) { + return firstWhitespaceAfterLineNumber; + } + + return -1; + } + + /** + * Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`. + * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found. + */ + public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number { + this._checkPendingChanges(); + lineNumber = lineNumber | 0; + + return this._findFirstWhitespaceAfterLineNumber(lineNumber); } /** @@ -124,6 +499,7 @@ export class LinesLayout { * @return The sum of heights for all objects above `lineNumber`. */ public getVerticalOffsetForLineNumber(lineNumber: number): number { + this._checkPendingChanges(); lineNumber = lineNumber | 0; let previousLinesHeight: number; @@ -133,36 +509,40 @@ export class LinesLayout { previousLinesHeight = 0; } - let previousWhitespacesHeight = this._whitespaces.getAccumulatedHeightBeforeLineNumber(lineNumber); - - return previousLinesHeight + previousWhitespacesHeight; - } + const previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber); - /** - * Returns the accumulated height of whitespaces before the given line number. - * - * @param lineNumber The line number - */ - public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number { - return this._whitespaces.getAccumulatedHeightBeforeLineNumber(lineNumber); + return previousLinesHeight + previousWhitespacesHeight + this._paddingTop; } /** * Returns if there is any whitespace in the document. */ public hasWhitespace(): boolean { - return this._whitespaces.getCount() > 0; + this._checkPendingChanges(); + return this.getWhitespacesCount() > 0; } + /** + * The maximum min width for all whitespaces. + */ public getWhitespaceMinWidth(): number { - return this._whitespaces.getMinWidth(); + this._checkPendingChanges(); + if (this._minWidth === -1) { + let minWidth = 0; + for (let i = 0, len = this._arr.length; i < len; i++) { + minWidth = Math.max(minWidth, this._arr[i].minWidth); + } + this._minWidth = minWidth; + } + return this._minWidth; } /** * Check if `verticalOffset` is below all lines. */ public isAfterLines(verticalOffset: number): boolean { - let totalHeight = this.getLinesTotalHeight(); + this._checkPendingChanges(); + const totalHeight = this.getLinesTotalHeight(); return verticalOffset > totalHeight; } @@ -175,6 +555,7 @@ export class LinesLayout { * @return The line number at or after vertical offset `verticalOffset`. */ public getLineNumberAtOrAfterVerticalOffset(verticalOffset: number): number { + this._checkPendingChanges(); verticalOffset = verticalOffset | 0; if (verticalOffset < 0) { @@ -187,9 +568,9 @@ export class LinesLayout { let maxLineNumber = linesCount; while (minLineNumber < maxLineNumber) { - let midLineNumber = ((minLineNumber + maxLineNumber) / 2) | 0; + const midLineNumber = ((minLineNumber + maxLineNumber) / 2) | 0; - let midLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(midLineNumber) | 0; + const midLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(midLineNumber) | 0; if (verticalOffset >= midLineNumberVerticalOffset + lineHeight) { // vertical offset is after mid line number @@ -218,6 +599,7 @@ export class LinesLayout { * @return A structure describing the lines positioned between `verticalOffset1` and `verticalOffset2`. */ public getLinesViewportData(verticalOffset1: number, verticalOffset2: number): IPartialViewLinesViewportData { + this._checkPendingChanges(); verticalOffset1 = verticalOffset1 | 0; verticalOffset2 = verticalOffset2 | 0; const lineHeight = this._lineHeight; @@ -230,8 +612,8 @@ export class LinesLayout { let endLineNumber = this._lineCount | 0; // Also keep track of what whitespace we've got - let whitespaceIndex = this._whitespaces.getFirstWhitespaceIndexAfterLineNumber(startLineNumber) | 0; - const whitespaceCount = this._whitespaces.getCount() | 0; + let whitespaceIndex = this.getFirstWhitespaceIndexAfterLineNumber(startLineNumber) | 0; + const whitespaceCount = this.getWhitespacesCount() | 0; let currentWhitespaceHeight: number; let currentWhitespaceAfterLineNumber: number; @@ -240,8 +622,8 @@ export class LinesLayout { currentWhitespaceAfterLineNumber = endLineNumber + 1; currentWhitespaceHeight = 0; } else { - currentWhitespaceAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; - currentWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceHeight = this.getHeightForWhitespaceIndex(whitespaceIndex) | 0; } let currentVerticalOffset = startLineNumberVerticalOffset; @@ -258,7 +640,7 @@ export class LinesLayout { currentLineRelativeOffset -= bigNumbersDelta; } - let linesOffsets: number[] = []; + const linesOffsets: number[] = []; const verticalCenter = verticalOffset1 + (verticalOffset2 - verticalOffset1) / 2; let centeredLineNumber = -1; @@ -267,8 +649,8 @@ export class LinesLayout { for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { if (centeredLineNumber === -1) { - let currentLineTop = currentVerticalOffset; - let currentLineBottom = currentVerticalOffset + lineHeight; + const currentLineTop = currentVerticalOffset; + const currentLineBottom = currentVerticalOffset + lineHeight; if ((currentLineTop <= verticalCenter && verticalCenter < currentLineBottom) || currentLineTop > verticalCenter) { centeredLineNumber = lineNumber; } @@ -291,8 +673,8 @@ export class LinesLayout { if (whitespaceIndex >= whitespaceCount) { currentWhitespaceAfterLineNumber = endLineNumber + 1; } else { - currentWhitespaceAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; - currentWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; + currentWhitespaceHeight = this.getHeightForWhitespaceIndex(whitespaceIndex) | 0; } } @@ -335,9 +717,10 @@ export class LinesLayout { } public getVerticalOffsetForWhitespaceIndex(whitespaceIndex: number): number { + this._checkPendingChanges(); whitespaceIndex = whitespaceIndex | 0; - let afterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(whitespaceIndex); + const afterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex); let previousLinesHeight: number; if (afterLineNumber >= 1) { @@ -348,38 +731,36 @@ export class LinesLayout { let previousWhitespacesHeight: number; if (whitespaceIndex > 0) { - previousWhitespacesHeight = this._whitespaces.getAccumulatedHeight(whitespaceIndex - 1); + previousWhitespacesHeight = this.getWhitespacesAccumulatedHeight(whitespaceIndex - 1); } else { previousWhitespacesHeight = 0; } - return previousLinesHeight + previousWhitespacesHeight; + return previousLinesHeight + previousWhitespacesHeight + this._paddingTop; } public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number { + this._checkPendingChanges(); verticalOffset = verticalOffset | 0; - let midWhitespaceIndex: number, - minWhitespaceIndex = 0, - maxWhitespaceIndex = this._whitespaces.getCount() - 1, - midWhitespaceVerticalOffset: number, - midWhitespaceHeight: number; + let minWhitespaceIndex = 0; + let maxWhitespaceIndex = this.getWhitespacesCount() - 1; if (maxWhitespaceIndex < 0) { return -1; } // Special case: nothing to be found - let maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex); - let maxWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(maxWhitespaceIndex); + const maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex); + const maxWhitespaceHeight = this.getHeightForWhitespaceIndex(maxWhitespaceIndex); if (verticalOffset >= maxWhitespaceVerticalOffset + maxWhitespaceHeight) { return -1; } while (minWhitespaceIndex < maxWhitespaceIndex) { - midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2); + const midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2); - midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex); - midWhitespaceHeight = this._whitespaces.getHeightForWhitespaceIndex(midWhitespaceIndex); + const midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex); + const midWhitespaceHeight = this.getHeightForWhitespaceIndex(midWhitespaceIndex); if (verticalOffset >= midWhitespaceVerticalOffset + midWhitespaceHeight) { // vertical offset is after whitespace @@ -402,27 +783,28 @@ export class LinesLayout { * @return Precisely the whitespace that is layouted at `verticaloffset` or null. */ public getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null { + this._checkPendingChanges(); verticalOffset = verticalOffset | 0; - let candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset); + const candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset); if (candidateIndex < 0) { return null; } - if (candidateIndex >= this._whitespaces.getCount()) { + if (candidateIndex >= this.getWhitespacesCount()) { return null; } - let candidateTop = this.getVerticalOffsetForWhitespaceIndex(candidateIndex); + const candidateTop = this.getVerticalOffsetForWhitespaceIndex(candidateIndex); if (candidateTop > verticalOffset) { return null; } - let candidateHeight = this._whitespaces.getHeightForWhitespaceIndex(candidateIndex); - let candidateId = this._whitespaces.getIdForWhitespaceIndex(candidateIndex); - let candidateAfterLineNumber = this._whitespaces.getAfterLineNumberForWhitespaceIndex(candidateIndex); + const candidateHeight = this.getHeightForWhitespaceIndex(candidateIndex); + const candidateId = this.getIdForWhitespaceIndex(candidateIndex); + const candidateAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(candidateIndex); return { id: candidateId, @@ -440,11 +822,12 @@ export class LinesLayout { * @return An array with all the whitespaces in the viewport. If no whitespace is in viewport, the array is empty. */ public getWhitespaceViewportData(verticalOffset1: number, verticalOffset2: number): IViewWhitespaceViewportData[] { + this._checkPendingChanges(); verticalOffset1 = verticalOffset1 | 0; verticalOffset2 = verticalOffset2 | 0; - let startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1); - let endIndex = this._whitespaces.getCount() - 1; + const startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1); + const endIndex = this.getWhitespacesCount() - 1; if (startIndex < 0) { return []; @@ -452,15 +835,15 @@ export class LinesLayout { let result: IViewWhitespaceViewportData[] = []; for (let i = startIndex; i <= endIndex; i++) { - let top = this.getVerticalOffsetForWhitespaceIndex(i); - let height = this._whitespaces.getHeightForWhitespaceIndex(i); + const top = this.getVerticalOffsetForWhitespaceIndex(i); + const height = this.getHeightForWhitespaceIndex(i); if (top >= verticalOffset2) { break; } result.push({ - id: this._whitespaces.getIdForWhitespaceIndex(i), - afterLineNumber: this._whitespaces.getAfterLineNumberForWhitespaceIndex(i), + id: this.getIdForWhitespaceIndex(i), + afterLineNumber: this.getAfterLineNumberForWhitespaceIndex(i), verticalOffset: top, height: height }); @@ -473,6 +856,54 @@ export class LinesLayout { * Get all whitespaces. */ public getWhitespaces(): IEditorWhitespace[] { - return this._whitespaces.getWhitespaces(this._lineHeight); + this._checkPendingChanges(); + return this._arr.slice(0); + } + + /** + * The number of whitespaces. + */ + public getWhitespacesCount(): number { + this._checkPendingChanges(); + return this._arr.length; + } + + /** + * Get the `id` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `id` of whitespace at `index`. + */ + public getIdForWhitespaceIndex(index: number): string { + this._checkPendingChanges(); + index = index | 0; + + return this._arr[index].id; + } + + /** + * Get the `afterLineNumber` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `afterLineNumber` of whitespace at `index`. + */ + public getAfterLineNumberForWhitespaceIndex(index: number): number { + this._checkPendingChanges(); + index = index | 0; + + return this._arr[index].afterLineNumber; + } + + /** + * Get the `height` for whitespace at index `index`. + * + * @param index The index of the whitespace. + * @return `height` of whitespace at `index`. + */ + public getHeightForWhitespaceIndex(index: number): number { + this._checkPendingChanges(); + index = index | 0; + + return this._arr[index].height; } } diff --git a/src/vs/editor/common/viewLayout/viewLayout.ts b/src/vs/editor/common/viewLayout/viewLayout.ts index 74fca28d02999..0964eac915f73 100644 --- a/src/vs/editor/common/viewLayout/viewLayout.ts +++ b/src/vs/editor/common/viewLayout/viewLayout.ts @@ -3,43 +3,177 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { IScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility, INewScrollPosition } from 'vs/base/common/scrollable'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LinesLayout } from 'vs/editor/common/viewLayout/linesLayout'; +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 { IEditorWhitespace } from 'vs/editor/common/viewLayout/whitespaceComputer'; import { IViewLayout, IViewWhitespaceViewportData, Viewport } from 'vs/editor/common/viewModel/viewModel'; +import { ContentSizeChangedEvent } from 'vs/editor/common/viewModel/viewModelEventDispatcher'; const SMOOTH_SCROLLING_TIME = 125; +class EditorScrollDimensions { + + public readonly width: number; + public readonly contentWidth: number; + public readonly scrollWidth: number; + + public readonly height: number; + public readonly contentHeight: number; + public readonly scrollHeight: number; + + constructor( + width: number, + contentWidth: number, + height: number, + contentHeight: number, + ) { + width = width | 0; + contentWidth = contentWidth | 0; + height = height | 0; + contentHeight = contentHeight | 0; + + if (width < 0) { + width = 0; + } + if (contentWidth < 0) { + contentWidth = 0; + } + + if (height < 0) { + height = 0; + } + if (contentHeight < 0) { + contentHeight = 0; + } + + this.width = width; + this.contentWidth = contentWidth; + this.scrollWidth = Math.max(width, contentWidth); + + this.height = height; + this.contentHeight = contentHeight; + this.scrollHeight = Math.max(height, contentHeight); + } + + public equals(other: EditorScrollDimensions): boolean { + return ( + this.width === other.width + && this.contentWidth === other.contentWidth + && this.height === other.height + && this.contentHeight === other.contentHeight + ); + } +} + +class EditorScrollable extends Disposable { + + private readonly _scrollable: Scrollable; + private _dimensions: EditorScrollDimensions; + + public readonly onDidScroll: Event; + + private readonly _onDidContentSizeChange = this._register(new Emitter()); + public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; + + constructor(smoothScrollDuration: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { + super(); + this._dimensions = new EditorScrollDimensions(0, 0, 0, 0); + this._scrollable = this._register(new Scrollable(smoothScrollDuration, scheduleAtNextAnimationFrame)); + this.onDidScroll = this._scrollable.onScroll; + } + + public getScrollable(): Scrollable { + return this._scrollable; + } + + public setSmoothScrollDuration(smoothScrollDuration: number): void { + this._scrollable.setSmoothScrollDuration(smoothScrollDuration); + } + + public validateScrollPosition(scrollPosition: INewScrollPosition): IScrollPosition { + return this._scrollable.validateScrollPosition(scrollPosition); + } + + public getScrollDimensions(): EditorScrollDimensions { + return this._dimensions; + } + + public setScrollDimensions(dimensions: EditorScrollDimensions): void { + if (this._dimensions.equals(dimensions)) { + return; + } + + const oldDimensions = this._dimensions; + this._dimensions = dimensions; + + this._scrollable.setScrollDimensions({ + width: dimensions.width, + 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(new ContentSizeChangedEvent( + oldDimensions.contentWidth, oldDimensions.contentHeight, + dimensions.contentWidth, dimensions.contentHeight + )); + } + } + + public getFutureScrollPosition(): IScrollPosition { + return this._scrollable.getFutureScrollPosition(); + } + + public getCurrentScrollPosition(): IScrollPosition { + return this._scrollable.getCurrentScrollPosition(); + } + + public setScrollPositionNow(update: INewScrollPosition): void { + this._scrollable.setScrollPositionNow(update); + } + + public setScrollPositionSmooth(update: INewScrollPosition): void { + this._scrollable.setScrollPositionSmooth(update); + } +} + export class ViewLayout extends Disposable implements IViewLayout { - private readonly _configuration: editorCommon.IConfiguration; + private readonly _configuration: IConfiguration; private readonly _linesLayout: LinesLayout; - public readonly scrollable: Scrollable; + private readonly _scrollable: EditorScrollable; public readonly onDidScroll: Event; + public readonly onDidContentSizeChange: Event; - constructor(configuration: editorCommon.IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { + constructor(configuration: IConfiguration, lineCount: number, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { super(); 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 Scrollable(0, scheduleAtNextAnimationFrame)); + this._scrollable = this._register(new EditorScrollable(0, scheduleAtNextAnimationFrame)); this._configureSmoothScrollDuration(); - this.scrollable.setScrollDimensions({ - width: layoutInfo.contentWidth, - height: layoutInfo.contentHeight - }); - this.onDidScroll = this.scrollable.onScroll; + this._scrollable.setScrollDimensions(new EditorScrollDimensions( + layoutInfo.contentWidth, + 0, + layoutInfo.height, + 0 + )); + this.onDidScroll = this._scrollable.onDidScroll; + this.onDidContentSizeChange = this._scrollable.onDidContentSizeChange; this._updateHeight(); } @@ -48,12 +182,16 @@ export class ViewLayout extends Disposable implements IViewLayout { super.dispose(); } + public getScrollable(): Scrollable { + return this._scrollable.getScrollable(); + } + public onHeightMaybeChanged(): void { this._updateHeight(); } private _configureSmoothScrollDuration(): void { - this.scrollable.setSmoothScrollDuration(this._configuration.options.get(EditorOption.smoothScrolling) ? SMOOTH_SCROLLING_TIME : 0); + this._scrollable.setSmoothScrollDuration(this._configuration.options.get(EditorOption.smoothScrolling) ? SMOOTH_SCROLLING_TIME : 0); } // ---- begin view event handlers @@ -63,19 +201,22 @@ 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.contentHeight; - const scrollDimensions = this.scrollable.getScrollDimensions(); - const scrollWidth = scrollDimensions.scrollWidth; - const scrollHeight = this._getTotalHeight(width, height, scrollWidth); - - this.scrollable.setScrollDimensions({ - width: width, - height: height, - scrollHeight: scrollHeight - }); + const height = layoutInfo.height; + const scrollDimensions = this._scrollable.getScrollDimensions(); + const contentWidth = scrollDimensions.contentWidth; + this._scrollable.setScrollDimensions(new EditorScrollDimensions( + width, + scrollDimensions.contentWidth, + height, + this._getContentHeight(width, height, contentWidth) + )); } else { this._updateHeight(); } @@ -109,35 +250,37 @@ export class ViewLayout extends Disposable implements IViewLayout { return scrollbar.horizontalScrollbarSize; } - private _getTotalHeight(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 Math.max(height, result); + return result; } private _updateHeight(): void { - const scrollDimensions = this.scrollable.getScrollDimensions(); + const scrollDimensions = this._scrollable.getScrollDimensions(); const width = scrollDimensions.width; const height = scrollDimensions.height; - const scrollWidth = scrollDimensions.scrollWidth; - const scrollHeight = this._getTotalHeight(width, height, scrollWidth); - this.scrollable.setScrollDimensions({ - scrollHeight: scrollHeight - }); + const contentWidth = scrollDimensions.contentWidth; + this._scrollable.setScrollDimensions(new EditorScrollDimensions( + width, + scrollDimensions.contentWidth, + height, + this._getContentHeight(width, height, contentWidth) + )); } // ---- Layouting logic public getCurrentViewport(): Viewport { - const scrollDimensions = this.scrollable.getScrollDimensions(); - const currentScrollPosition = this.scrollable.getCurrentScrollPosition(); + const scrollDimensions = this._scrollable.getScrollDimensions(); + const currentScrollPosition = this._scrollable.getCurrentScrollPosition(); return new Viewport( currentScrollPosition.scrollTop, currentScrollPosition.scrollLeft, @@ -147,8 +290,8 @@ export class ViewLayout extends Disposable implements IViewLayout { } public getFutureViewport(): Viewport { - const scrollDimensions = this.scrollable.getScrollDimensions(); - const currentScrollPosition = this.scrollable.getFutureScrollPosition(); + const scrollDimensions = this._scrollable.getScrollDimensions(); + const currentScrollPosition = this._scrollable.getFutureScrollPosition(); return new Viewport( currentScrollPosition.scrollTop, currentScrollPosition.scrollLeft, @@ -157,23 +300,37 @@ export class ViewLayout extends Disposable implements IViewLayout { ); } - private _computeScrollWidth(maxLineWidth: number, viewportWidth: number): number { + 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, viewportWidth, whitespaceMinWidth); + return Math.max(maxLineWidth + extraHorizontalSpace, whitespaceMinWidth); } - return Math.max(maxLineWidth, viewportWidth); } - public onMaxLineWidthChanged(maxLineWidth: number): void { - let newScrollWidth = this._computeScrollWidth(maxLineWidth, this.getCurrentViewport().width); - this.scrollable.setScrollDimensions({ - scrollWidth: newScrollWidth - }); + public setMaxLineWidth(maxLineWidth: number): void { + const scrollDimensions = this._scrollable.getScrollDimensions(); + // const newScrollWidth = ; + this._scrollable.setScrollDimensions(new EditorScrollDimensions( + scrollDimensions.width, + this._computeContentWidth(maxLineWidth), + scrollDimensions.height, + scrollDimensions.contentHeight + )); // The height might depend on the fact that there is a horizontal scrollbar or not this._updateHeight(); @@ -182,7 +339,7 @@ export class ViewLayout extends Disposable implements IViewLayout { // ---- view state public saveState(): { scrollTop: number; scrollTopWithoutViewZones: number; scrollLeft: number; } { - const currentScrollPosition = this.scrollable.getFutureScrollPosition(); + const currentScrollPosition = this._scrollable.getFutureScrollPosition(); let scrollTop = currentScrollPosition.scrollTop; let firstLineNumberInViewport = this._linesLayout.getLineNumberAtOrAfterVerticalOffset(scrollTop); let whitespaceAboveFirstLine = this._linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(firstLineNumberInViewport); @@ -194,15 +351,12 @@ export class ViewLayout extends Disposable implements IViewLayout { } // ---- IVerticalLayoutProvider - - public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string { - return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height, minWidth); - } - public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { - return this._linesLayout.changeWhitespace(id, newAfterLineNumber, newHeight); - } - public removeWhitespace(id: string): boolean { - return this._linesLayout.removeWhitespace(id); + 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); @@ -223,7 +377,7 @@ export class ViewLayout extends Disposable implements IViewLayout { } public getLinesViewportDataAtScrollTop(scrollTop: number): IPartialViewLinesViewportData { // do some minimal validations on scrollTop - const scrollDimensions = this.scrollable.getScrollDimensions(); + const scrollDimensions = this._scrollable.getScrollDimensions(); if (scrollTop + scrollDimensions.height > scrollDimensions.scrollHeight) { scrollTop = scrollDimensions.scrollHeight - scrollDimensions.height; } @@ -242,40 +396,47 @@ export class ViewLayout extends Disposable implements IViewLayout { // ---- IScrollingProvider - + public getContentWidth(): number { + const scrollDimensions = this._scrollable.getScrollDimensions(); + return scrollDimensions.contentWidth; + } public getScrollWidth(): number { - const scrollDimensions = this.scrollable.getScrollDimensions(); + const scrollDimensions = this._scrollable.getScrollDimensions(); return scrollDimensions.scrollWidth; } + public getContentHeight(): number { + const scrollDimensions = this._scrollable.getScrollDimensions(); + return scrollDimensions.contentHeight; + } public getScrollHeight(): number { - const scrollDimensions = this.scrollable.getScrollDimensions(); + const scrollDimensions = this._scrollable.getScrollDimensions(); return scrollDimensions.scrollHeight; } public getCurrentScrollLeft(): number { - const currentScrollPosition = this.scrollable.getCurrentScrollPosition(); + const currentScrollPosition = this._scrollable.getCurrentScrollPosition(); return currentScrollPosition.scrollLeft; } public getCurrentScrollTop(): number { - const currentScrollPosition = this.scrollable.getCurrentScrollPosition(); + const currentScrollPosition = this._scrollable.getCurrentScrollPosition(); return currentScrollPosition.scrollTop; } - public validateScrollPosition(scrollPosition: editorCommon.INewScrollPosition): IScrollPosition { - return this.scrollable.validateScrollPosition(scrollPosition); + public validateScrollPosition(scrollPosition: INewScrollPosition): IScrollPosition { + return this._scrollable.validateScrollPosition(scrollPosition); } - public setScrollPositionNow(position: editorCommon.INewScrollPosition): void { - this.scrollable.setScrollPositionNow(position); - } - - public setScrollPositionSmooth(position: editorCommon.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 { - const currentScrollPosition = this.scrollable.getCurrentScrollPosition(); - this.scrollable.setScrollPositionNow({ + const currentScrollPosition = this._scrollable.getCurrentScrollPosition(); + this._scrollable.setScrollPositionNow({ scrollLeft: currentScrollPosition.scrollLeft + deltaScrollLeft, scrollTop: currentScrollPosition.scrollTop + deltaScrollTop }); diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 5298d234529bc..23640e0af3b52 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); } } @@ -66,7 +83,10 @@ export class RenderLineInput { public readonly lineTokens: IViewLineTokens; public readonly lineDecorations: LineDecoration[]; public readonly tabSize: number; + public readonly startVisibleColumn: number; public readonly spaceWidth: number; + public readonly renderSpaceWidth: number; + public readonly renderSpaceCharCode: number; public readonly stopRenderingLineAfter: number; public readonly renderWhitespace: RenderWhitespace; public readonly renderControlCharacters: boolean; @@ -89,9 +109,12 @@ export class RenderLineInput { lineTokens: IViewLineTokens, lineDecorations: LineDecoration[], tabSize: number, + 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 @@ -106,6 +129,7 @@ export class RenderLineInput { this.lineTokens = lineTokens; this.lineDecorations = lineDecorations; this.tabSize = tabSize; + this.startVisibleColumn = startVisibleColumn; this.spaceWidth = spaceWidth; this.stopRenderingLineAfter = stopRenderingLineAfter; this.renderWhitespace = ( @@ -115,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 { @@ -154,7 +190,10 @@ export class RenderLineInput { && this.containsRTL === other.containsRTL && this.fauxIndentLength === other.fauxIndentLength && 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 @@ -314,21 +353,24 @@ export function renderViewLine(input: RenderLineInput, sb: IStringBuilder): Rend if (input.lineDecorations.length > 0) { // This line is empty, but it contains inline decorations - let classNames: string[] = []; + const beforeClassNames: string[] = []; + const afterClassNames: string[] = []; for (let i = 0, len = input.lineDecorations.length; i < len; i++) { const lineDecoration = input.lineDecorations[i]; if (lineDecoration.type === InlineDecorationType.Before) { - classNames.push(input.lineDecorations[i].className); + beforeClassNames.push(input.lineDecorations[i].className); containsForeignElements |= ForeignElementType.Before; } if (lineDecoration.type === InlineDecorationType.After) { - classNames.push(input.lineDecorations[i].className); + afterClassNames.push(input.lineDecorations[i].className); containsForeignElements |= ForeignElementType.After; } } if (containsForeignElements !== ForeignElementType.None) { - content = ``; + const beforeSpan = (beforeClassNames.length > 0 ? `` : ``); + const afterSpan = (afterClassNames.length > 0 ? `` : ``); + content = `${beforeSpan}${afterSpan}`; } } @@ -368,9 +410,12 @@ class ResolvedRenderLineInput { public readonly isOverflowing: boolean, public readonly parts: LinePart[], public readonly containsForeignElements: ForeignElementType, + public readonly fauxIndentLength: number, public readonly tabSize: number, + public readonly startVisibleColumn: number, public readonly containsRTL: boolean, public readonly spaceWidth: number, + public readonly renderSpaceCharCode: number, public readonly renderWhitespace: RenderWhitespace, public readonly renderControlCharacters: boolean, ) { @@ -379,7 +424,6 @@ class ResolvedRenderLineInput { } function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput { - const useMonospaceOptimizations = input.useMonospaceOptimizations; const lineContent = input.lineContent; let isOverflowing: boolean; @@ -394,8 +438,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, 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) { @@ -418,16 +466,19 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput } return new ResolvedRenderLineInput( - useMonospaceOptimizations, + input.useMonospaceOptimizations, input.canUseHalfwidthRightwardsArrow, lineContent, len, isOverflowing, tokens, containsForeignElements, + input.fauxIndentLength, input.tabSize, + input.startVisibleColumn, input.containsRTL, input.spaceWidth, + input.renderSpaceCharCode, input.renderWhitespace, input.renderControlCharacters ); @@ -442,7 +493,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++) { @@ -453,10 +504,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; @@ -485,6 +536,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; @@ -494,13 +546,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; @@ -516,12 +568,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; } @@ -533,11 +586,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, 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; @@ -545,31 +608,21 @@ 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 { lastNonWhitespaceIndex = strings.lastNonWhitespaceIndex(lineContent); } - let tmpIndent = 0; - for (let charIndex = 0; charIndex < fauxIndentLength; charIndex++) { - const chCode = lineContent.charCodeAt(charIndex); - if (chCode === CharCode.Tab) { - tmpIndent = tabSize; - } else if (strings.isFullWidthCharacter(chCode)) { - tmpIndent += 2; - } else { - tmpIndent++; - } - } - tmpIndent = tmpIndent % tabSize; let wasInWhitespace = false; let currentSelectionIndex = 0; let currentSelection = selections && selections[currentSelectionIndex]; + let tmpIndent = startVisibleColumn % tabSize; for (let charIndex = fauxIndentLength; charIndex < len; charIndex++) { const chCode = lineContent.charCodeAt(charIndex); @@ -607,17 +660,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; } } @@ -632,7 +697,7 @@ function _applyRenderWhitespace(lineContent: string, len: number, continuesWithW wasInWhitespace = isInWhitespace; - if (charIndex === tokenEndIndex) { + while (charIndex === tokenEndIndex) { tokenIndex++; if (tokenIndex < tokensLength) { tokenType = tokens[tokenIndex].type; @@ -656,7 +721,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; } @@ -676,42 +752,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; @@ -729,22 +808,30 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render const len = input.len; const isOverflowing = input.isOverflowing; const parts = input.parts; + const fauxIndentLength = input.fauxIndentLength; const tabSize = input.tabSize; + const startVisibleColumn = input.startVisibleColumn; const containsRTL = input.containsRTL; const spaceWidth = input.spaceWidth; + const renderSpaceCharCode = input.renderSpaceCharCode; const renderWhitespace = input.renderWhitespace; const renderControlCharacters = input.renderControlCharacters; const characterMapping = new CharacterMapping(len + 1, parts.length); let charIndex = 0; - let tabsCharDelta = 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; @@ -752,11 +839,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(' 0) { - if (!canUseHalfwidthRightwardsArrow || insertSpacesCount > 1) { - sb.write1(0x2192); // RIGHTWARDS ARROW - } else { - sb.write1(0xFFEB); // HALFWIDTH RIGHTWARDS ARROW - } - insertSpacesCount--; + charWidth = (tabSize - (visibleColumn % tabSize)) | 0; + + if (!canUseHalfwidthRightwardsArrow || charWidth > 1) { + sb.write1(0x2192); // RIGHTWARDS ARROW + } else { + sb.write1(0xFFEB); // HALFWIDTH RIGHTWARDS ARROW } - while (insertSpacesCount > 0) { + for (let space = 2; space <= charWidth; space++) { sb.write1(0xA0); //   - insertSpacesCount--; } - } else { - // must be CharCode.Space - sb.write1(0xB7); // · + + } else { // must be CharCode.Space + charWidth = 1; + + sb.write1(renderSpaceCharCode); // · or word separator middle dot } - charOffsetInPart++; + charOffsetInPart += charWidth; + if (charIndex >= fauxIndentLength) { + visibleColumn += charWidth; + } } prevPartContentCnt = partContentCnt; @@ -824,77 +908,79 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render let partContentCnt = 0; - if (containsRTL) { - sb.appendASCIIString(' dir="ltr"'); - } sb.appendASCII(CharCode.GreaterThan); for (; charIndex < partEndIndex; charIndex++) { - characterMapping.setPartData(charIndex, partIndex, charOffsetInPart, partAbsoluteOffset); + characterMapping.setPartData(charIndex, partIndex - partDisplacement, charOffsetInPart, partAbsoluteOffset); + partDisplacement = 0; const charCode = lineContent.charCodeAt(charIndex); + let producedCharacters = 1; + let charWidth = 1; + switch (charCode) { case CharCode.Tab: - let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize; - tabsCharDelta += insertSpacesCount - 1; - charOffsetInPart += insertSpacesCount - 1; - while (insertSpacesCount > 0) { + producedCharacters = (tabSize - (visibleColumn % tabSize)); + charWidth = producedCharacters; + for (let space = 1; space <= producedCharacters; space++) { sb.write1(0xA0); //   - partContentCnt++; - insertSpacesCount--; } break; case CharCode.Space: sb.write1(0xA0); //   - partContentCnt++; break; case CharCode.LessThan: sb.appendASCIIString('<'); - partContentCnt++; break; case CharCode.GreaterThan: sb.appendASCIIString('>'); - partContentCnt++; break; case CharCode.Ampersand: sb.appendASCIIString('&'); - partContentCnt++; break; case CharCode.Null: sb.appendASCIIString('�'); - partContentCnt++; break; case CharCode.UTF8_BOM: - case CharCode.LINE_SEPARATOR_2028: + case CharCode.LINE_SEPARATOR: + case CharCode.PARAGRAPH_SEPARATOR: + case CharCode.NEXT_LINE: sb.write1(0xFFFD); - partContentCnt++; break; default: if (strings.isFullWidthCharacter(charCode)) { - tabsCharDelta++; + charWidth++; } if (renderControlCharacters && charCode < 32) { sb.write1(9216 + charCode); - partContentCnt++; } else { sb.write1(charCode); - partContentCnt++; } } - charOffsetInPart++; + charOffsetInPart += producedCharacters; + partContentCnt += producedCharacters; + if (charIndex >= fauxIndentLength) { + visibleColumn += charWidth; + } } prevPartContentCnt = partContentCnt; } + if (partIsEmptyAndHasPseudoAfter) { + partDisplacement++; + } else { + partDisplacement = 0; + } + sb.appendASCIIString(''); } diff --git a/src/vs/editor/common/viewLayout/whitespaceComputer.ts b/src/vs/editor/common/viewLayout/whitespaceComputer.ts deleted file mode 100644 index 8e5dd347e8306..0000000000000 --- a/src/vs/editor/common/viewLayout/whitespaceComputer.ts +++ /dev/null @@ -1,493 +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 strings from 'vs/base/common/strings'; - -export interface IEditorWhitespace { - readonly id: string; - readonly afterLineNumber: number; - readonly heightInLines: number; -} - -/** - * Represent whitespaces in between lines and provide fast CRUD management methods. - * The whitespaces are sorted ascending by `afterLineNumber`. - */ -export class WhitespaceComputer { - - private static INSTANCE_COUNT = 0; - - private readonly _instanceId: string; - - /** - * heights[i] is the height in pixels for whitespace at index i - */ - private readonly _heights: number[]; - - /** - * minWidths[i] is the min width in pixels for whitespace at index i - */ - private readonly _minWidths: number[]; - - /** - * afterLineNumbers[i] is the line number whitespace at index i is after - */ - private readonly _afterLineNumbers: number[]; - - /** - * ordinals[i] is the orinal of the whitespace at index i - */ - private readonly _ordinals: number[]; - - /** - * prefixSum[i] = SUM(heights[j]), 1 <= j <= i - */ - private readonly _prefixSum: number[]; - - /** - * prefixSum[i], 1 <= i <= prefixSumValidIndex can be trusted - */ - private _prefixSumValidIndex: number; - - /** - * ids[i] is the whitespace id of whitespace at index i - */ - private readonly _ids: string[]; - - /** - * index at which a whitespace is positioned (inside heights, afterLineNumbers, prefixSum members) - */ - private readonly _whitespaceId2Index: { - [id: string]: number; - }; - - /** - * last whitespace id issued - */ - private _lastWhitespaceId: number; - - private _minWidth: number; - - constructor() { - this._instanceId = strings.singleLetterHash(++WhitespaceComputer.INSTANCE_COUNT); - this._heights = []; - this._minWidths = []; - this._ids = []; - this._afterLineNumbers = []; - this._ordinals = []; - this._prefixSum = []; - this._prefixSumValidIndex = -1; - this._whitespaceId2Index = {}; - this._lastWhitespaceId = 0; - this._minWidth = -1; /* marker for not being computed */ - } - - /** - * Find the insertion index for a new value inside a sorted array of values. - * If the value is already present in the sorted array, the insertion index will be after the already existing value. - */ - public static findInsertionIndex(sortedArray: number[], value: number, ordinals: number[], valueOrdinal: number): number { - let low = 0; - let high = sortedArray.length; - - while (low < high) { - let mid = ((low + high) >>> 1); - - if (value === sortedArray[mid]) { - if (valueOrdinal < ordinals[mid]) { - high = mid; - } else { - low = mid + 1; - } - } else if (value < sortedArray[mid]) { - high = mid; - } else { - low = mid + 1; - } - } - - return low; - } - - /** - * Insert a new whitespace of a certain height after a line number. - * The whitespace has a "sticky" characteristic. - * Irrespective of edits above or below `afterLineNumber`, the whitespace will follow the initial line. - * - * @param afterLineNumber The conceptual position of this whitespace. The whitespace will follow this line as best as possible even when deleting/inserting lines above/below. - * @param heightInPx The height of the whitespace, in pixels. - * @return An id that can be used later to mutate or delete the whitespace - */ - public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string { - afterLineNumber = afterLineNumber | 0; - ordinal = ordinal | 0; - heightInPx = heightInPx | 0; - minWidth = minWidth | 0; - - let id = this._instanceId + (++this._lastWhitespaceId); - let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal); - this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx, minWidth); - this._minWidth = -1; /* marker for not being computed */ - return id; - } - - private _insertWhitespaceAtIndex(id: string, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void { - insertIndex = insertIndex | 0; - afterLineNumber = afterLineNumber | 0; - ordinal = ordinal | 0; - heightInPx = heightInPx | 0; - minWidth = minWidth | 0; - - this._heights.splice(insertIndex, 0, heightInPx); - this._minWidths.splice(insertIndex, 0, minWidth); - this._ids.splice(insertIndex, 0, id); - this._afterLineNumbers.splice(insertIndex, 0, afterLineNumber); - this._ordinals.splice(insertIndex, 0, ordinal); - this._prefixSum.splice(insertIndex, 0, 0); - - let keys = Object.keys(this._whitespaceId2Index); - for (let i = 0, len = keys.length; i < len; i++) { - let sid = keys[i]; - let oldIndex = this._whitespaceId2Index[sid]; - if (oldIndex >= insertIndex) { - this._whitespaceId2Index[sid] = oldIndex + 1; - } - } - - this._whitespaceId2Index[id] = insertIndex; - this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1); - } - - /** - * Change properties associated with a certain whitespace. - */ - public changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean { - newAfterLineNumber = newAfterLineNumber | 0; - newHeight = newHeight | 0; - - let hasChanges = false; - hasChanges = this.changeWhitespaceHeight(id, newHeight) || hasChanges; - hasChanges = this.changeWhitespaceAfterLineNumber(id, newAfterLineNumber) || hasChanges; - return hasChanges; - } - - /** - * Change the height of an existing whitespace - * - * @param id The whitespace to change - * @param newHeightInPx The new height of the whitespace, in pixels - * @return Returns true if the whitespace is found and if the new height is different than the old height - */ - public changeWhitespaceHeight(id: string, newHeightInPx: number): boolean { - newHeightInPx = newHeightInPx | 0; - - if (this._whitespaceId2Index.hasOwnProperty(id)) { - let index = this._whitespaceId2Index[id]; - if (this._heights[index] !== newHeightInPx) { - this._heights[index] = newHeightInPx; - this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1); - return true; - } - } - return false; - } - - /** - * Change the line number after which an existing whitespace flows. - * - * @param id The whitespace to change - * @param newAfterLineNumber The new line number the whitespace will follow - * @return Returns true if the whitespace is found and if the new line number is different than the old line number - */ - public changeWhitespaceAfterLineNumber(id: string, newAfterLineNumber: number): boolean { - newAfterLineNumber = newAfterLineNumber | 0; - - if (this._whitespaceId2Index.hasOwnProperty(id)) { - let index = this._whitespaceId2Index[id]; - if (this._afterLineNumbers[index] !== newAfterLineNumber) { - // `afterLineNumber` changed for this whitespace - - // Record old ordinal - let ordinal = this._ordinals[index]; - - // Record old height - let heightInPx = this._heights[index]; - - // Record old min width - let minWidth = this._minWidths[index]; - - // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace - this.removeWhitespace(id); - - // And add it again - let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal); - this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx, minWidth); - - return true; - } - } - return false; - } - - /** - * Remove an existing whitespace. - * - * @param id The whitespace to remove - * @return Returns true if the whitespace is found and it is removed. - */ - public removeWhitespace(id: string): boolean { - if (this._whitespaceId2Index.hasOwnProperty(id)) { - let index = this._whitespaceId2Index[id]; - delete this._whitespaceId2Index[id]; - this._removeWhitespaceAtIndex(index); - this._minWidth = -1; /* marker for not being computed */ - return true; - } - - return false; - } - - private _removeWhitespaceAtIndex(removeIndex: number): void { - removeIndex = removeIndex | 0; - - this._heights.splice(removeIndex, 1); - this._minWidths.splice(removeIndex, 1); - this._ids.splice(removeIndex, 1); - this._afterLineNumbers.splice(removeIndex, 1); - this._ordinals.splice(removeIndex, 1); - this._prefixSum.splice(removeIndex, 1); - this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1); - - let keys = Object.keys(this._whitespaceId2Index); - for (let i = 0, len = keys.length; i < len; i++) { - let sid = keys[i]; - let oldIndex = this._whitespaceId2Index[sid]; - if (oldIndex >= removeIndex) { - this._whitespaceId2Index[sid] = oldIndex - 1; - } - } - } - - /** - * Notify the computer that lines have been deleted (a continuous zone of lines). - * This gives it a chance to update `afterLineNumber` for whitespaces, giving the "sticky" characteristic. - * - * @param fromLineNumber The line number at which the deletion started, inclusive - * @param toLineNumber The line number at which the deletion ended, inclusive - */ - public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void { - fromLineNumber = fromLineNumber | 0; - toLineNumber = toLineNumber | 0; - - for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { - let afterLineNumber = this._afterLineNumbers[i]; - - if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) { - // The line this whitespace was after has been deleted - // => move whitespace to before first deleted line - this._afterLineNumbers[i] = fromLineNumber - 1; - } else if (afterLineNumber > toLineNumber) { - // The line this whitespace was after has been moved up - // => move whitespace up - this._afterLineNumbers[i] -= (toLineNumber - fromLineNumber + 1); - } - } - } - - /** - * Notify the computer that lines have been inserted (a continuous zone of lines). - * This gives it a chance to update `afterLineNumber` for whitespaces, giving the "sticky" characteristic. - * - * @param fromLineNumber The line number at which the insertion started, inclusive - * @param toLineNumber The line number at which the insertion ended, inclusive. - */ - public onLinesInserted(fromLineNumber: number, toLineNumber: number): void { - fromLineNumber = fromLineNumber | 0; - toLineNumber = toLineNumber | 0; - - for (let i = 0, len = this._afterLineNumbers.length; i < len; i++) { - let afterLineNumber = this._afterLineNumbers[i]; - - if (fromLineNumber <= afterLineNumber) { - this._afterLineNumbers[i] += (toLineNumber - fromLineNumber + 1); - } - } - } - - /** - * Get the sum of all the whitespaces. - */ - public getTotalHeight(): number { - if (this._heights.length === 0) { - return 0; - } - return this.getAccumulatedHeight(this._heights.length - 1); - } - - /** - * Return the sum of the heights of the whitespaces at [0..index]. - * This includes the whitespace at `index`. - * - * @param index The index of the whitespace. - * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`. - */ - public getAccumulatedHeight(index: number): number { - index = index | 0; - - let startIndex = Math.max(0, this._prefixSumValidIndex + 1); - if (startIndex === 0) { - this._prefixSum[0] = this._heights[0]; - startIndex++; - } - - for (let i = startIndex; i <= index; i++) { - this._prefixSum[i] = this._prefixSum[i - 1] + this._heights[i]; - } - this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index); - return this._prefixSum[index]; - } - - /** - * Find all whitespaces with `afterLineNumber` < `lineNumber` and return the sum of their heights. - * - * @param lineNumber The line number whitespaces should be before. - * @return The sum of the heights of the whitespaces before `lineNumber`. - */ - public getAccumulatedHeightBeforeLineNumber(lineNumber: number): number { - lineNumber = lineNumber | 0; - - let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); - - if (lastWhitespaceBeforeLineNumber === -1) { - return 0; - } - - return this.getAccumulatedHeight(lastWhitespaceBeforeLineNumber); - } - - private _findLastWhitespaceBeforeLineNumber(lineNumber: number): number { - lineNumber = lineNumber | 0; - - // Find the whitespace before line number - let afterLineNumbers = this._afterLineNumbers; - let low = 0; - let high = afterLineNumbers.length - 1; - - while (low <= high) { - let delta = (high - low) | 0; - let halfDelta = (delta / 2) | 0; - let mid = (low + halfDelta) | 0; - - if (afterLineNumbers[mid] < lineNumber) { - if (mid + 1 >= afterLineNumbers.length || afterLineNumbers[mid + 1] >= lineNumber) { - return mid; - } else { - low = (mid + 1) | 0; - } - } else { - high = (mid - 1) | 0; - } - } - - return -1; - } - - private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number { - lineNumber = lineNumber | 0; - - let lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber); - let firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1; - - if (firstWhitespaceAfterLineNumber < this._heights.length) { - return firstWhitespaceAfterLineNumber; - } - - return -1; - } - - /** - * Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`. - * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found. - */ - public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number { - lineNumber = lineNumber | 0; - - return this._findFirstWhitespaceAfterLineNumber(lineNumber); - } - - /** - * The number of whitespaces. - */ - public getCount(): number { - return this._heights.length; - } - - /** - * The maximum min width for all whitespaces. - */ - public getMinWidth(): number { - if (this._minWidth === -1) { - let minWidth = 0; - for (let i = 0, len = this._minWidths.length; i < len; i++) { - minWidth = Math.max(minWidth, this._minWidths[i]); - } - this._minWidth = minWidth; - } - return this._minWidth; - } - - /** - * Get the `afterLineNumber` for whitespace at index `index`. - * - * @param index The index of the whitespace. - * @return `afterLineNumber` of whitespace at `index`. - */ - public getAfterLineNumberForWhitespaceIndex(index: number): number { - index = index | 0; - - return this._afterLineNumbers[index]; - } - - /** - * Get the `id` for whitespace at index `index`. - * - * @param index The index of the whitespace. - * @return `id` of whitespace at `index`. - */ - public getIdForWhitespaceIndex(index: number): string { - index = index | 0; - - return this._ids[index]; - } - - /** - * Get the `height` for whitespace at index `index`. - * - * @param index The index of the whitespace. - * @return `height` of whitespace at `index`. - */ - public getHeightForWhitespaceIndex(index: number): number { - index = index | 0; - - return this._heights[index]; - } - - /** - * Get all whitespaces. - */ - public getWhitespaces(deviceLineHeight: number): IEditorWhitespace[] { - deviceLineHeight = deviceLineHeight | 0; - - let result: IEditorWhitespace[] = []; - for (let i = 0; i < this._heights.length; i++) { - result.push({ - id: this._ids[i], - afterLineNumber: this._afterLineNumbers[i], - heightInLines: this._heights[i] / deviceLineHeight - }); - } - return result; - } -} diff --git a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts b/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts deleted file mode 100644 index bbf71e817b93f..0000000000000 --- a/src/vs/editor/common/viewModel/characterHardWrappingLineMapper.ts +++ /dev/null @@ -1,293 +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 { CharCode } from 'vs/base/common/charCode'; -import * as strings from 'vs/base/common/strings'; -import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; -import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; -import { toUint32Array } from 'vs/base/common/uint'; -import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; -import { ILineMapperFactory, ILineMapping, OutputPosition } from 'vs/editor/common/viewModel/splitLinesCollection'; - -const enum CharacterClass { - NONE = 0, - BREAK_BEFORE = 1, - BREAK_AFTER = 2, - BREAK_OBTRUSIVE = 3, - BREAK_IDEOGRAPHIC = 4 // for Han and Kana. -} - -class WrappingCharacterClassifier extends CharacterClassifier { - - constructor(BREAK_BEFORE: string, BREAK_AFTER: string, BREAK_OBTRUSIVE: string) { - super(CharacterClass.NONE); - - for (let i = 0; i < BREAK_BEFORE.length; i++) { - this.set(BREAK_BEFORE.charCodeAt(i), CharacterClass.BREAK_BEFORE); - } - - for (let i = 0; i < BREAK_AFTER.length; i++) { - this.set(BREAK_AFTER.charCodeAt(i), CharacterClass.BREAK_AFTER); - } - - for (let i = 0; i < BREAK_OBTRUSIVE.length; i++) { - this.set(BREAK_OBTRUSIVE.charCodeAt(i), CharacterClass.BREAK_OBTRUSIVE); - } - } - - public get(charCode: number): CharacterClass { - // Initialize CharacterClass.BREAK_IDEOGRAPHIC for these Unicode ranges: - // 1. CJK Unified Ideographs (0x4E00 -- 0x9FFF) - // 2. CJK Unified Ideographs Extension A (0x3400 -- 0x4DBF) - // 3. Hiragana and Katakana (0x3040 -- 0x30FF) - if ( - (charCode >= 0x3040 && charCode <= 0x30FF) - || (charCode >= 0x3400 && charCode <= 0x4DBF) - || (charCode >= 0x4E00 && charCode <= 0x9FFF) - ) { - return CharacterClass.BREAK_IDEOGRAPHIC; - } - - return super.get(charCode); - } -} - -export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactory { - - private readonly classifier: WrappingCharacterClassifier; - - constructor(breakBeforeChars: string, breakAfterChars: string, breakObtrusiveChars: string) { - this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars, breakObtrusiveChars); - } - - // TODO@Alex -> duplicated in lineCommentCommand - private static nextVisibleColumn(currentVisibleColumn: number, tabSize: number, isTab: boolean, columnSize: number): number { - currentVisibleColumn = +currentVisibleColumn; //@perf - tabSize = +tabSize; //@perf - columnSize = +columnSize; //@perf - - if (isTab) { - return currentVisibleColumn + (tabSize - (currentVisibleColumn % tabSize)); - } - return currentVisibleColumn + columnSize; - } - - public createLineMapping(lineText: string, tabSize: number, breakingColumn: number, columnsForFullWidthChar: number, hardWrappingIndent: WrappingIndent): ILineMapping | null { - if (breakingColumn === -1) { - return null; - } - - tabSize = +tabSize; //@perf - breakingColumn = +breakingColumn; //@perf - columnsForFullWidthChar = +columnsForFullWidthChar; //@perf - hardWrappingIndent = +hardWrappingIndent; //@perf - - let wrappedTextIndentVisibleColumn = 0; - let wrappedTextIndent = ''; - - let firstNonWhitespaceIndex = -1; - if (hardWrappingIndent !== WrappingIndent.None) { - firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText); - if (firstNonWhitespaceIndex !== -1) { - // Track existing indent - wrappedTextIndent = lineText.substring(0, firstNonWhitespaceIndex); - for (let i = 0; i < firstNonWhitespaceIndex; i++) { - wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, lineText.charCodeAt(i) === CharCode.Tab, 1); - } - - // Increase indent of continuation lines, if desired - let numberOfAdditionalTabs = 0; - if (hardWrappingIndent === WrappingIndent.Indent) { - numberOfAdditionalTabs = 1; - } else if (hardWrappingIndent === WrappingIndent.DeepIndent) { - numberOfAdditionalTabs = 2; - } - for (let i = 0; i < numberOfAdditionalTabs; i++) { - wrappedTextIndent += '\t'; - wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, true, 1); - } - - // Force sticking to beginning of line if no character would fit except for the indentation - if (wrappedTextIndentVisibleColumn + columnsForFullWidthChar > breakingColumn) { - wrappedTextIndent = ''; - wrappedTextIndentVisibleColumn = 0; - } - } - } - - let classifier = this.classifier; - let lastBreakingOffset = 0; // Last 0-based offset in the lineText at which a break happened - let breakingLengths: number[] = []; // The length of each broken-up line text - let breakingLengthsIndex: number = 0; // The count of breaks already done - let visibleColumn = 0; // Visible column since the beginning of the current line - let niceBreakOffset = -1; // Last index of a character that indicates a break should happen before it (more desirable) - let niceBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `niceBreakOffset` - let obtrusiveBreakOffset = -1; // Last index of a character that indicates a break should happen before it (less desirable) - let obtrusiveBreakVisibleColumn = 0; // visible column if a break were to be later introduced before `obtrusiveBreakOffset` - let len = lineText.length; - - for (let i = 0; i < len; i++) { - // At this point, there is a certainty that the character before `i` fits on the current line, - // but the character at `i` might not fit - - let charCode = lineText.charCodeAt(i); - let charCodeIsTab = (charCode === CharCode.Tab); - let charCodeClass = classifier.get(charCode); - - if (strings.isLowSurrogate(charCode)/* && i + 1 < len */) { - // A surrogate pair must always be considered as a single unit, so it is never to be broken - // => advance visibleColumn by 1 and advance to next char code... - visibleColumn = visibleColumn + 1; - continue; - } - - if (charCodeClass === CharacterClass.BREAK_BEFORE) { - // This is a character that indicates that a break should happen before it - // Since we are certain the character before `i` fits, there's no extra checking needed, - // just mark it as a nice breaking opportunity - niceBreakOffset = i; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } - - // CJK breaking : before break - if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i > 0) { - let prevCode = lineText.charCodeAt(i - 1); - let prevClass = classifier.get(prevCode); - if (prevClass !== CharacterClass.BREAK_BEFORE) { // Kinsoku Shori: Don't break after a leading character, like an open bracket - niceBreakOffset = i; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } - } - - let charColumnSize = 1; - if (strings.isFullWidthCharacter(charCode)) { - charColumnSize = columnsForFullWidthChar; - } - - // Advance visibleColumn with character at `i` - visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(visibleColumn, tabSize, charCodeIsTab, charColumnSize); - - if (visibleColumn > breakingColumn && i !== 0) { - // We need to break at least before character at `i`: - // - break before niceBreakLastOffset if it exists (and re-establish a correct visibleColumn by using niceBreakVisibleColumn + charAt(i)) - // - otherwise, break before obtrusiveBreakLastOffset if it exists (and re-establish a correct visibleColumn by using obtrusiveBreakVisibleColumn + charAt(i)) - // - otherwise, break before i (and re-establish a correct visibleColumn by charAt(i)) - - let breakBeforeOffset: number; - let restoreVisibleColumnFrom: number; - - if (niceBreakOffset !== -1 && niceBreakVisibleColumn <= breakingColumn) { - - // We will break before `niceBreakLastOffset` - breakBeforeOffset = niceBreakOffset; - restoreVisibleColumnFrom = niceBreakVisibleColumn; - - } else if (obtrusiveBreakOffset !== -1 && obtrusiveBreakVisibleColumn <= breakingColumn) { - - // We will break before `obtrusiveBreakLastOffset` - breakBeforeOffset = obtrusiveBreakOffset; - restoreVisibleColumnFrom = obtrusiveBreakVisibleColumn; - - } else { - - // We will break before `i` - breakBeforeOffset = i; - restoreVisibleColumnFrom = wrappedTextIndentVisibleColumn; - - } - - // Break before character at `breakBeforeOffset` - breakingLengths[breakingLengthsIndex++] = breakBeforeOffset - lastBreakingOffset; - lastBreakingOffset = breakBeforeOffset; - - // Re-establish visibleColumn by taking character at `i` into account - visibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(restoreVisibleColumnFrom, tabSize, charCodeIsTab, charColumnSize); - - // Reset markers - niceBreakOffset = -1; - niceBreakVisibleColumn = 0; - obtrusiveBreakOffset = -1; - obtrusiveBreakVisibleColumn = 0; - } - - // At this point, there is a certainty that the character at `i` fits on the current line - - if (niceBreakOffset !== -1) { - // Advance niceBreakVisibleColumn - niceBreakVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(niceBreakVisibleColumn, tabSize, charCodeIsTab, charColumnSize); - } - if (obtrusiveBreakOffset !== -1) { - // Advance obtrusiveBreakVisibleColumn - obtrusiveBreakVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(obtrusiveBreakVisibleColumn, tabSize, charCodeIsTab, charColumnSize); - } - - if (charCodeClass === CharacterClass.BREAK_AFTER && (hardWrappingIndent === WrappingIndent.None || i >= firstNonWhitespaceIndex)) { - // This is a character that indicates that a break should happen after it - niceBreakOffset = i + 1; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } - - // CJK breaking : after break - if (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && i < len - 1) { - let nextCode = lineText.charCodeAt(i + 1); - let nextClass = classifier.get(nextCode); - if (nextClass !== CharacterClass.BREAK_AFTER) { // Kinsoku Shori: Don't break before a trailing character, like a period - niceBreakOffset = i + 1; - niceBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } - } - - if (charCodeClass === CharacterClass.BREAK_OBTRUSIVE) { - // This is an obtrusive character that indicates that a break should happen after it - obtrusiveBreakOffset = i + 1; - obtrusiveBreakVisibleColumn = wrappedTextIndentVisibleColumn; - } - } - - if (breakingLengthsIndex === 0) { - return null; - } - - // Add last segment - breakingLengths[breakingLengthsIndex++] = len - lastBreakingOffset; - - return new CharacterHardWrappingLineMapping( - new PrefixSumComputer(toUint32Array(breakingLengths)), - wrappedTextIndent - ); - } -} - -export class CharacterHardWrappingLineMapping implements ILineMapping { - - private readonly _prefixSums: PrefixSumComputer; - private readonly _wrappedLinesIndent: string; - - constructor(prefixSums: PrefixSumComputer, wrappedLinesIndent: string) { - this._prefixSums = prefixSums; - this._wrappedLinesIndent = wrappedLinesIndent; - } - - public getOutputLineCount(): number { - return this._prefixSums.getCount(); - } - - public getWrappedLinesIndent(): string { - return this._wrappedLinesIndent; - } - - public getInputOffsetOfOutputPosition(outputLineIndex: number, outputOffset: number): number { - if (outputLineIndex === 0) { - return outputOffset; - } else { - return this._prefixSums.getAccumulatedValue(outputLineIndex - 1) + outputOffset; - } - } - - public getOutputPositionOfInputOffset(inputOffset: number): OutputPosition { - let r = this._prefixSums.getIndexOf(inputOffset); - return new OutputPosition(r.index, r.remainder); - } -} diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts new file mode 100644 index 0000000000000..549496d003a23 --- /dev/null +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -0,0 +1,486 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from 'vs/base/common/charCode'; +import * as strings from 'vs/base/common/strings'; +import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; +import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; +import { ILineBreaksComputerFactory, LineBreakData, ILineBreaksComputer } from 'vs/editor/common/viewModel/splitLinesCollection'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; + +const enum CharacterClass { + NONE = 0, + BREAK_BEFORE = 1, + BREAK_AFTER = 2, + BREAK_IDEOGRAPHIC = 3 // for Han and Kana. +} + +class WrappingCharacterClassifier extends CharacterClassifier { + + constructor(BREAK_BEFORE: string, BREAK_AFTER: string) { + super(CharacterClass.NONE); + + for (let i = 0; i < BREAK_BEFORE.length; i++) { + this.set(BREAK_BEFORE.charCodeAt(i), CharacterClass.BREAK_BEFORE); + } + + for (let i = 0; i < BREAK_AFTER.length; i++) { + this.set(BREAK_AFTER.charCodeAt(i), CharacterClass.BREAK_AFTER); + } + } + + public get(charCode: number): CharacterClass { + if (charCode >= 0 && charCode < 256) { + return this._asciiMap[charCode]; + } else { + // Initialize CharacterClass.BREAK_IDEOGRAPHIC for these Unicode ranges: + // 1. CJK Unified Ideographs (0x4E00 -- 0x9FFF) + // 2. CJK Unified Ideographs Extension A (0x3400 -- 0x4DBF) + // 3. Hiragana and Katakana (0x3040 -- 0x30FF) + if ( + (charCode >= 0x3040 && charCode <= 0x30FF) + || (charCode >= 0x3400 && charCode <= 0x4DBF) + || (charCode >= 0x4E00 && charCode <= 0x9FFF) + ) { + return CharacterClass.BREAK_IDEOGRAPHIC; + } + + return (this._map.get(charCode) || this._defaultValue); + } + } +} + +let arrPool1: number[] = []; +let arrPool2: number[] = []; + +export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFactory { + + public static create(options: IComputedEditorOptions): MonospaceLineBreaksComputerFactory { + return new MonospaceLineBreaksComputerFactory( + options.get(EditorOption.wordWrapBreakBeforeCharacters), + options.get(EditorOption.wordWrapBreakAfterCharacters) + ); + } + + private readonly classifier: WrappingCharacterClassifier; + + constructor(breakBeforeChars: string, breakAfterChars: string) { + this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars); + } + + public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { + tabSize = tabSize | 0; //@perf + wrappingColumn = +wrappingColumn; //@perf + + let requests: string[] = []; + let previousBreakingData: (LineBreakData | null)[] = []; + return { + addRequest: (lineText: string, previousLineBreakData: LineBreakData | null) => { + requests.push(lineText); + previousBreakingData.push(previousLineBreakData); + }, + finalize: () => { + const columnsForFullWidthChar = fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth; //@perf + let result: (LineBreakData | null)[] = []; + for (let i = 0, len = requests.length; i < len; i++) { + const previousLineBreakData = previousBreakingData[i]; + if (previousLineBreakData) { + result[i] = createLineBreaksFromPreviousLineBreaks(this.classifier, previousLineBreakData, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + } else { + result[i] = createLineBreaks(this.classifier, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + } + } + arrPool1.length = 0; + arrPool2.length = 0; + return result; + } + }; + } +} + +function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterClassifier, previousBreakingData: LineBreakData, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): LineBreakData | null { + if (firstLineBreakColumn === -1) { + return null; + } + + const len = lineText.length; + if (len <= 1) { + return null; + } + + const prevBreakingOffsets = previousBreakingData.breakOffsets; + const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakOffsetsVisibleColumn; + + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakColumn, columnsForFullWidthChar, wrappingIndent); + const wrappedLineBreakColumn = firstLineBreakColumn - wrappedTextIndentLength; + + 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; + let prevIndex = 0; + + if (prevIndex >= 0) { + let bestDistance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); + while (prevIndex + 1 < prevLen) { + const distance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (distance >= bestDistance) { + break; + } + bestDistance = distance; + prevIndex++; + } + } + + while (prevIndex < prevLen) { + // Allow for prevIndex to be -1 (for the case where we hit a tab when walking backwards from the first break) + 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; + + let forcedBreakOffset = 0; + let forcedBreakOffsetVisibleColumn = 0; + + // initially, we search as much as possible to the right (if it fits) + 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; + const charCode = lineText.charCodeAt(i); + let charCodeClass: number; + let charWidth: number; + + if (strings.isHighSurrogate(charCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + i++; + charCodeClass = CharacterClass.NONE; + charWidth = 2; + } else { + charCodeClass = classifier.get(charCode); + charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); + } + + if (charStartOffset > lastBreakingOffset && canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { + breakOffset = charStartOffset; + breakOffsetVisibleColumn = visibleColumn; + } + + visibleColumn += charWidth; + + // 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`: + 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 + breakOffset = 0; + } + + entireLineFits = false; + break; + } + + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; + } + + if (entireLineFits) { + // there is no more need to break => stop the outer loop! + if (breakingOffsetsCount > 0) { + // Add last segment, no need to assign to `lastBreakingOffset` and `lastBreakingOffsetVisibleColumn` + breakingOffsets[breakingOffsetsCount] = prevBreakingOffsets[prevBreakingOffsets.length - 1]; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = prevBreakingOffsetsVisibleColumn[prevBreakingOffsets.length - 1]; + breakingOffsetsCount++; + } + break; + } + } + + if (breakOffset === 0) { + // must search left + let visibleColumn = prevBreakOffsetVisibleColumn; + let charCode = lineText.charCodeAt(prevBreakOffset); + let charCodeClass = classifier.get(charCode); + let hitATabCharacter = false; + for (let i = prevBreakOffset - 1; i >= lastBreakingOffset; i--) { + const charStartOffset = i + 1; + const prevCharCode = lineText.charCodeAt(i); + + if (prevCharCode === CharCode.Tab) { + // cannot determine the width of a tab when going backwards, so we must go forwards + hitATabCharacter = true; + break; + } + + let prevCharCodeClass: number; + let prevCharWidth: number; + + if (strings.isLowSurrogate(prevCharCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + i--; + prevCharCodeClass = CharacterClass.NONE; + prevCharWidth = 2; + } else { + prevCharCodeClass = classifier.get(prevCharCode); + prevCharWidth = (strings.isFullWidthCharacter(prevCharCode) ? columnsForFullWidthChar : 1); + } + + if (visibleColumn <= breakingColumn) { + if (forcedBreakOffset === 0) { + forcedBreakOffset = charStartOffset; + forcedBreakOffsetVisibleColumn = visibleColumn; + } + + if (visibleColumn <= breakingColumn - wrappedLineBreakColumn) { + // went too far! + break; + } + + if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { + breakOffset = charStartOffset; + breakOffsetVisibleColumn = visibleColumn; + break; + } + } + + visibleColumn -= prevCharWidth; + charCode = prevCharCode; + charCodeClass = prevCharCodeClass; + } + + if (breakOffset !== 0) { + const remainingWidthOfNextLine = wrappedLineBreakColumn - (forcedBreakOffsetVisibleColumn - breakOffsetVisibleColumn); + if (remainingWidthOfNextLine <= tabSize) { + const charCodeAtForcedBreakOffset = lineText.charCodeAt(forcedBreakOffset); + let charWidth: number; + if (strings.isHighSurrogate(charCodeAtForcedBreakOffset)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + charWidth = 2; + } else { + charWidth = computeCharWidth(charCodeAtForcedBreakOffset, forcedBreakOffsetVisibleColumn, tabSize, columnsForFullWidthChar); + } + if (remainingWidthOfNextLine - charWidth < 0) { + // it is not worth it to break at breakOffset, it just introduces an extra needless line! + breakOffset = 0; + } + } + } + + if (hitATabCharacter) { + // cannot determine the width of a tab when going backwards, so we must go forwards from the previous break + prevIndex--; + continue; + } + } + + if (breakOffset === 0) { + // Could not find a good breaking point + breakOffset = forcedBreakOffset; + breakOffsetVisibleColumn = forcedBreakOffsetVisibleColumn; + } + + lastBreakingOffset = breakOffset; + breakingOffsets[breakingOffsetsCount] = breakOffset; + lastBreakingOffsetVisibleColumn = breakOffsetVisibleColumn; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn; + + while (prevIndex < 0 || (prevIndex < prevLen && prevBreakingOffsetsVisibleColumn[prevIndex] < breakOffsetVisibleColumn)) { + prevIndex++; + } + + let bestDistance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex] - breakingColumn); + while (prevIndex + 1 < prevLen) { + const distance = Math.abs(prevBreakingOffsetsVisibleColumn[prevIndex + 1] - breakingColumn); + if (distance >= bestDistance) { + break; + } + bestDistance = distance; + prevIndex++; + } + } + + if (breakingOffsetsCount === 0) { + return null; + } + + // Doing here some object reuse which ends up helping a huge deal with GC pauses! + breakingOffsets.length = breakingOffsetsCount; + breakingOffsetsVisibleColumn.length = breakingOffsetsCount; + arrPool1 = previousBreakingData.breakOffsets; + arrPool2 = previousBreakingData.breakOffsetsVisibleColumn; + previousBreakingData.breakOffsets = breakingOffsets; + previousBreakingData.breakOffsetsVisibleColumn = breakingOffsetsVisibleColumn; + previousBreakingData.wrappedTextIndentLength = wrappedTextIndentLength; + return previousBreakingData; +} + +function createLineBreaks(classifier: WrappingCharacterClassifier, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): LineBreakData | null { + if (firstLineBreakColumn === -1) { + return null; + } + + const len = lineText.length; + if (len <= 1) { + return null; + } + + const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakColumn, columnsForFullWidthChar, wrappingIndent); + const wrappedLineBreakColumn = firstLineBreakColumn - wrappedTextIndentLength; + + let breakingOffsets: number[] = []; + let breakingOffsetsVisibleColumn: number[] = []; + let breakingOffsetsCount: number = 0; + let breakOffset = 0; + let breakOffsetVisibleColumn = 0; + + let breakingColumn = firstLineBreakColumn; + let prevCharCode = lineText.charCodeAt(0); + let prevCharCodeClass = classifier.get(prevCharCode); + let visibleColumn = computeCharWidth(prevCharCode, 0, tabSize, columnsForFullWidthChar); + + let startOffset = 1; + if (strings.isHighSurrogate(prevCharCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + visibleColumn += 1; + prevCharCode = lineText.charCodeAt(1); + prevCharCodeClass = classifier.get(prevCharCode); + startOffset++; + } + + for (let i = startOffset; i < len; i++) { + const charStartOffset = i; + const charCode = lineText.charCodeAt(i); + let charCodeClass: number; + let charWidth: number; + + if (strings.isHighSurrogate(charCode)) { + // A surrogate pair must always be considered as a single unit, so it is never to be broken + i++; + charCodeClass = CharacterClass.NONE; + charWidth = 2; + } else { + charCodeClass = classifier.get(charCode); + charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); + } + + if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { + breakOffset = charStartOffset; + breakOffsetVisibleColumn = visibleColumn; + } + + visibleColumn += charWidth; + + // 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`: + + if (breakOffset === 0 || visibleColumn - breakOffsetVisibleColumn > wrappedLineBreakColumn) { + // Cannot break at `breakOffset`, must break at `i` + breakOffset = charStartOffset; + breakOffsetVisibleColumn = visibleColumn - charWidth; + } + + breakingOffsets[breakingOffsetsCount] = breakOffset; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = breakOffsetVisibleColumn; + breakingOffsetsCount++; + breakingColumn = breakOffsetVisibleColumn + wrappedLineBreakColumn; + breakOffset = 0; + } + + prevCharCode = charCode; + prevCharCodeClass = charCodeClass; + } + + if (breakingOffsetsCount === 0) { + return null; + } + + // Add last segment + breakingOffsets[breakingOffsetsCount] = len; + breakingOffsetsVisibleColumn[breakingOffsetsCount] = visibleColumn; + + return new LineBreakData(breakingOffsets, breakingOffsetsVisibleColumn, wrappedTextIndentLength); +} + +function computeCharWidth(charCode: number, visibleColumn: number, tabSize: number, columnsForFullWidthChar: number): number { + if (charCode === CharCode.Tab) { + return (tabSize - (visibleColumn % tabSize)); + } + if (strings.isFullWidthCharacter(charCode)) { + return columnsForFullWidthChar; + } + return 1; +} + +function tabCharacterWidth(visibleColumn: number, tabSize: number): number { + return (tabSize - (visibleColumn % tabSize)); +} + +/** + * Kinsoku Shori : Don't break after a leading character, like an open bracket + * Kinsoku Shori : Don't break before a trailing character, like a period + */ +function canBreak(prevCharCode: number, prevCharCodeClass: CharacterClass, charCode: number, charCodeClass: CharacterClass): boolean { + return ( + charCode !== CharCode.Space + && ( + (prevCharCodeClass === CharacterClass.BREAK_AFTER) + || (prevCharCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && charCodeClass !== CharacterClass.BREAK_AFTER) + || (charCodeClass === CharacterClass.BREAK_BEFORE) + || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && prevCharCodeClass !== CharacterClass.BREAK_BEFORE) + ) + ); +} + +function computeWrappedTextIndentLength(lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): number { + let wrappedTextIndentLength = 0; + if (wrappingIndent !== WrappingIndent.None) { + const firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText); + if (firstNonWhitespaceIndex !== -1) { + // Track existing indent + + for (let i = 0; i < firstNonWhitespaceIndex; i++) { + const charWidth = (lineText.charCodeAt(i) === CharCode.Tab ? tabCharacterWidth(wrappedTextIndentLength, tabSize) : 1); + wrappedTextIndentLength += charWidth; + } + + // Increase indent of continuation lines, if desired + const numberOfAdditionalTabs = (wrappingIndent === WrappingIndent.DeepIndent ? 2 : wrappingIndent === WrappingIndent.Indent ? 1 : 0); + for (let i = 0; i < numberOfAdditionalTabs; i++) { + const charWidth = tabCharacterWidth(wrappedTextIndentLength, tabSize); + wrappedTextIndentLength += charWidth; + } + + // Force sticking to beginning of line if no character would fit except for the indentation + if (wrappedTextIndentLength + columnsForFullWidthChar > firstLineBreakColumn) { + wrappedTextIndentLength = 0; + } + } + } + return wrappedTextIndentLength; +} diff --git a/src/vs/editor/common/viewModel/prefixSumComputer.ts b/src/vs/editor/common/viewModel/prefixSumComputer.ts index d4e8c1f0df259..6cb1312c07e2d 100644 --- a/src/vs/editor/common/viewModel/prefixSumComputer.ts +++ b/src/vs/editor/common/viewModel/prefixSumComputer.ts @@ -187,73 +187,3 @@ export class PrefixSumComputer { return new PrefixSumIndexOfResult(mid, accumulatedValue - midStart); } } - -export class PrefixSumComputerWithCache { - - private readonly _actual: PrefixSumComputer; - private _cacheAccumulatedValueStart: number = 0; - private _cache: PrefixSumIndexOfResult[] | null = null; - - constructor(values: Uint32Array) { - this._actual = new PrefixSumComputer(values); - this._bustCache(); - } - - private _bustCache(): void { - this._cacheAccumulatedValueStart = 0; - this._cache = null; - } - - public insertValues(insertIndex: number, insertValues: Uint32Array): void { - if (this._actual.insertValues(insertIndex, insertValues)) { - this._bustCache(); - } - } - - public changeValue(index: number, value: number): void { - if (this._actual.changeValue(index, value)) { - this._bustCache(); - } - } - - public removeValues(startIndex: number, cnt: number): void { - if (this._actual.removeValues(startIndex, cnt)) { - this._bustCache(); - } - } - - public getTotalValue(): number { - return this._actual.getTotalValue(); - } - - public getAccumulatedValue(index: number): number { - return this._actual.getAccumulatedValue(index); - } - - public getIndexOf(accumulatedValue: number): PrefixSumIndexOfResult { - accumulatedValue = Math.floor(accumulatedValue); //@perf - - if (this._cache !== null) { - let cacheIndex = accumulatedValue - this._cacheAccumulatedValueStart; - if (cacheIndex >= 0 && cacheIndex < this._cache.length) { - // Cache hit! - return this._cache[cacheIndex]; - } - } - - // Cache miss! - return this._actual.getIndexOf(accumulatedValue); - } - - /** - * Gives a hint that a lot of requests are about to come in for these accumulated values. - */ - public warmUpCache(accumulatedValueStart: number, accumulatedValueEnd: number): void { - let newCache: PrefixSumIndexOfResult[] = []; - for (let accumulatedValue = accumulatedValueStart; accumulatedValue <= accumulatedValueEnd; accumulatedValue++) { - newCache[accumulatedValue - accumulatedValueStart] = this.getIndexOf(accumulatedValue); - } - this._cache = newCache; - this._cacheAccumulatedValueStart = accumulatedValueStart; - } -} diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 3cd2afbafaf85..3a3eab70a1c29 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as arrays from 'vs/base/common/arrays'; import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; import { Position } from 'vs/editor/common/core/position'; @@ -10,13 +11,13 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; -import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer'; +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 { - _outputPositionBrand: void; outputLineIndex: number; outputOffset: number; @@ -26,15 +27,56 @@ export class OutputPosition { } } -export interface ILineMapping { - getOutputLineCount(): number; - getWrappedLinesIndent(): string; - getInputOffsetOfOutputPosition(outputLineIndex: number, outputOffset: number): number; - getOutputPositionOfInputOffset(inputOffset: number): OutputPosition; +export class LineBreakData { + constructor( + public breakOffsets: number[], + public breakOffsetsVisibleColumn: number[], + public wrappedTextIndentLength: number + ) { } + + public static getInputOffsetOfOutputPosition(breakOffsets: number[], outputLineIndex: number, outputOffset: number): number { + if (outputLineIndex === 0) { + return outputOffset; + } else { + return breakOffsets[outputLineIndex - 1] + outputOffset; + } + } + + public static getOutputPositionOfInputOffset(breakOffsets: number[], inputOffset: number): OutputPosition { + let low = 0; + let high = breakOffsets.length - 1; + let mid = 0; + let midStart = 0; + + while (low <= high) { + mid = low + ((high - low) / 2) | 0; + + const midStop = breakOffsets[mid]; + midStart = mid > 0 ? breakOffsets[mid - 1] : 0; + + if (inputOffset < midStart) { + high = mid - 1; + } else if (inputOffset >= midStop) { + low = mid + 1; + } else { + break; + } + } + + return new OutputPosition(mid, inputOffset - midStart); + } } -export interface ILineMapperFactory { - createLineMapping(lineText: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ILineMapping | null; +export interface ILineBreaksComputer { + /** + * Pass in `previousLineBreakData` if the only difference is in breaking columns!!! + */ + addRequest(lineText: string, previousLineBreakData: LineBreakData | null): void; + finalize(): (LineBreakData | null)[]; +} + +export interface ILineBreaksComputerFactory { + createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer; } export interface ISimpleModel { @@ -50,6 +92,7 @@ export interface ISplitLine { isVisible(): boolean; setVisible(isVisible: boolean): ISplitLine; + getLineBreakData(): LineBreakData | null; getViewLineCount(): number; getViewLineContent(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): string; getViewLineLength(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): number; @@ -66,19 +109,19 @@ export interface ISplitLine { export interface IViewModelLinesCollection extends IDisposable { createCoordinatesConverter(): ICoordinatesConverter; - setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean; + setWrappingSettings(fontInfo: FontInfo, wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean; setTabSize(newTabSize: number): boolean; getHiddenAreas(): Range[]; setHiddenAreas(_ranges: Range[]): boolean; + createLineBreaksComputer(): ILineBreaksComputer; onModelFlushed(): void; onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent | null; - onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent | null; - onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null]; + onModelLinesInserted(versionId: number, fromLineNumber: number, toLineNumber: number, lineBreaks: (LineBreakData | null)[]): viewEvents.ViewLinesInsertedEvent | null; + onModelLineChanged(versionId: number, lineNumber: number, lineBreakData: LineBreakData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null]; acceptVersionId(versionId: number): void; getViewLineCount(): number; - warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void; getActiveIndentGuide(viewLineNumber: number, minLineNumber: number, maxLineNumber: number): IActiveIndentGuideInfo; getViewLinesIndentGuides(viewStartLineNumber: number, viewEndLineNumber: number): number[]; getViewLineContent(viewLineNumber: number): string; @@ -88,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[]; } @@ -107,9 +150,7 @@ export class CoordinatesConverter implements ICoordinatesConverter { } public convertViewRangeToModelRange(viewRange: Range): Range { - let start = this._lines.convertViewPositionToModelPosition(viewRange.startLineNumber, viewRange.startColumn); - let end = this._lines.convertViewPositionToModelPosition(viewRange.endLineNumber, viewRange.endColumn); - return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + return this._lines.convertViewRangeToModelRange(viewRange); } public validateViewPosition(viewPosition: Position, expectedModelPosition: Position): Position { @@ -117,9 +158,7 @@ export class CoordinatesConverter implements ICoordinatesConverter { } public validateViewRange(viewRange: Range, expectedModelRange: Range): Range { - const validViewStart = this._lines.validateViewPosition(viewRange.startLineNumber, viewRange.startColumn, expectedModelRange.getStartPosition()); - const validViewEnd = this._lines.validateViewPosition(viewRange.endLineNumber, viewRange.endColumn, expectedModelRange.getEndPosition()); - return new Range(validViewStart.lineNumber, validViewStart.column, validViewEnd.lineNumber, validViewEnd.column); + return this._lines.validateViewRange(viewRange, expectedModelRange); } // Model -> View conversion and related methods @@ -135,7 +174,6 @@ export class CoordinatesConverter implements ICoordinatesConverter { public modelPositionIsVisible(modelPosition: Position): boolean { return this._lines.modelPositionIsVisible(modelPosition.lineNumber, modelPosition.column); } - } const enum IndentGuideRepeatOption { @@ -144,33 +182,129 @@ const enum IndentGuideRepeatOption { BlockAll = 2 } +class LineNumberMapper { + + private _counts: number[]; + private _isValid: boolean; + private _validEndIndex: number; + + private _modelToView: number[]; + private _viewToModel: number[]; + + constructor(viewLineCounts: number[]) { + this._counts = viewLineCounts; + this._isValid = false; + this._validEndIndex = -1; + this._modelToView = []; + this._viewToModel = []; + } + + private _invalidate(index: number): void { + this._isValid = false; + this._validEndIndex = Math.min(this._validEndIndex, index - 1); + } + + private _ensureValid(): void { + if (this._isValid) { + return; + } + + for (let i = this._validEndIndex + 1, len = this._counts.length; i < len; i++) { + const viewLineCount = this._counts[i]; + const viewLinesAbove = (i > 0 ? this._modelToView[i - 1] : 0); + + this._modelToView[i] = viewLinesAbove + viewLineCount; + for (let j = 0; j < viewLineCount; j++) { + this._viewToModel[viewLinesAbove + j] = i; + } + } + + // trim things + this._modelToView.length = this._counts.length; + this._viewToModel.length = this._modelToView[this._modelToView.length - 1]; + + // mark as valid + this._isValid = true; + this._validEndIndex = this._counts.length - 1; + } + + public changeValue(index: number, value: number): void { + if (this._counts[index] === value) { + // no change + return; + } + this._counts[index] = value; + this._invalidate(index); + } + + public removeValues(start: number, deleteCount: number): void { + this._counts.splice(start, deleteCount); + this._invalidate(start); + } + + public insertValues(insertIndex: number, insertArr: number[]): void { + this._counts = arrays.arrayInsert(this._counts, insertIndex, insertArr); + this._invalidate(insertIndex); + } + + public getTotalValue(): number { + this._ensureValid(); + return this._viewToModel.length; + } + + public getAccumulatedValue(index: number): number { + this._ensureValid(); + return this._modelToView[index]; + } + + public getIndexOf(accumulatedValue: number): PrefixSumIndexOfResult { + this._ensureValid(); + const modelLineIndex = this._viewToModel[accumulatedValue]; + const viewLinesAbove = (modelLineIndex > 0 ? this._modelToView[modelLineIndex - 1] : 0); + return new PrefixSumIndexOfResult(modelLineIndex, accumulatedValue - viewLinesAbove); + } +} + export class SplitLinesCollection implements IViewModelLinesCollection { private readonly model: ITextModel; private _validModelVersionId: number; + private readonly _domLineBreaksComputerFactory: ILineBreaksComputerFactory; + private readonly _monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory; + + private fontInfo: FontInfo; + private tabSize: number; private wrappingColumn: number; - private columnsForFullWidthChar: number; private wrappingIndent: WrappingIndent; - private tabSize: number; + private wrappingStrategy: 'simple' | 'advanced'; private lines!: ISplitLine[]; - private prefixSumComputer!: PrefixSumComputerWithCache; - - private readonly linePositionMapperFactory: ILineMapperFactory; + private prefixSumComputer!: LineNumberMapper; private hiddenAreasIds!: string[]; - constructor(model: ITextModel, linePositionMapperFactory: ILineMapperFactory, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent) { + constructor( + model: ITextModel, + domLineBreaksComputerFactory: ILineBreaksComputerFactory, + monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory, + fontInfo: FontInfo, + tabSize: number, + wrappingStrategy: 'simple' | 'advanced', + wrappingColumn: number, + wrappingIndent: WrappingIndent, + ) { this.model = model; this._validModelVersionId = -1; + this._domLineBreaksComputerFactory = domLineBreaksComputerFactory; + this._monospaceLineBreaksComputerFactory = monospaceLineBreaksComputerFactory; + this.fontInfo = fontInfo; this.tabSize = tabSize; + this.wrappingStrategy = wrappingStrategy; this.wrappingColumn = wrappingColumn; - this.columnsForFullWidthChar = columnsForFullWidthChar; this.wrappingIndent = wrappingIndent; - this.linePositionMapperFactory = linePositionMapperFactory; - this._constructLines(true); + this._constructLines(/*resetHiddenAreas*/true, null); } public dispose(): void { @@ -181,19 +315,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new CoordinatesConverter(this); } - private _ensureValidState(): void { - let modelVersion = this.model.getVersionId(); - if (modelVersion !== this._validModelVersionId) { - // This is pretty bad, it means we lost track of the model... - throw new Error(`ViewModel is out of sync with Model!`); - } - if (this.lines.length !== this.model.getLineCount()) { - // This is pretty bad, it means we lost track of the model... - this._constructLines(false); - } - } - - private _constructLines(resetHiddenAreas: boolean): void { + private _constructLines(resetHiddenAreas: boolean, previousLineBreaks: ((LineBreakData | null)[]) | null): void { this.lines = []; if (resetHiddenAreas) { @@ -201,8 +323,14 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } let linesContent = this.model.getLinesContent(); - let lineCount = linesContent.length; - let values = new Uint32Array(lineCount); + const lineCount = linesContent.length; + const lineBreaksComputer = this.createLineBreaksComputer(); + for (let i = 0; i < lineCount; i++) { + lineBreaksComputer.addRequest(linesContent[i], previousLineBreaks ? previousLineBreaks[i] : null); + } + const linesBreaks = lineBreaksComputer.finalize(); + + let values: number[] = []; let hiddenAreas = this.hiddenAreasIds.map((areaId) => this.model.getDecorationRange(areaId)!).sort(Range.compareRangesUsingStarts); let hiddenAreaStart = 1, hiddenAreaEnd = 0; @@ -220,14 +348,14 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } let isInHiddenArea = (lineNumber >= hiddenAreaStart && lineNumber <= hiddenAreaEnd); - let line = createSplitLine(this.linePositionMapperFactory, linesContent[i], this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, !isInHiddenArea); + let line = createSplitLine(linesBreaks[i], !isInHiddenArea); values[i] = line.getViewLineCount(); this.lines[i] = line; } this._validModelVersionId = this.model.getVersionId(); - this.prefixSumComputer = new PrefixSumComputerWithCache(values); + this.prefixSumComputer = new LineNumberMapper(values); } public getHiddenAreas(): Range[] { @@ -351,27 +479,51 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } this.tabSize = newTabSize; - this._constructLines(false); + this._constructLines(/*resetHiddenAreas*/false, null); return true; } - public setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean { - if (this.wrappingIndent === wrappingIndent && this.wrappingColumn === wrappingColumn && this.columnsForFullWidthChar === columnsForFullWidthChar) { + public setWrappingSettings(fontInfo: FontInfo, wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean { + const equalFontInfo = this.fontInfo.equals(fontInfo); + const equalWrappingStrategy = (this.wrappingStrategy === wrappingStrategy); + const equalWrappingColumn = (this.wrappingColumn === wrappingColumn); + const equalWrappingIndent = (this.wrappingIndent === wrappingIndent); + if (equalFontInfo && equalWrappingStrategy && equalWrappingColumn && equalWrappingIndent) { return false; } - this.wrappingIndent = wrappingIndent; + const onlyWrappingColumnChanged = (equalFontInfo && equalWrappingStrategy && !equalWrappingColumn && equalWrappingIndent); + + this.fontInfo = fontInfo; + this.wrappingStrategy = wrappingStrategy; this.wrappingColumn = wrappingColumn; - this.columnsForFullWidthChar = columnsForFullWidthChar; + this.wrappingIndent = wrappingIndent; + + let previousLineBreaks: ((LineBreakData | null)[]) | null = null; + if (onlyWrappingColumnChanged) { + previousLineBreaks = []; + for (let i = 0, len = this.lines.length; i < len; i++) { + previousLineBreaks[i] = this.lines[i].getLineBreakData(); + } + } - this._constructLines(false); + this._constructLines(/*resetHiddenAreas*/false, previousLineBreaks); return true; } + public createLineBreaksComputer(): ILineBreaksComputer { + const lineBreaksComputerFactory = ( + this.wrappingStrategy === 'advanced' + ? this._domLineBreaksComputerFactory + : this._monospaceLineBreaksComputerFactory + ); + return lineBreaksComputerFactory.createLineBreaksComputer(this.fontInfo, this.tabSize, this.wrappingColumn, this.wrappingIndent); + } + public onModelFlushed(): void { - this._constructLines(true); + this._constructLines(/*resetHiddenAreas*/true, null); } public onModelLinesDeleted(versionId: number, fromLineNumber: number, toLineNumber: number): viewEvents.ViewLinesDeletedEvent | null { @@ -390,7 +542,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesDeletedEvent(outputFromLineNumber, outputToLineNumber); } - public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, text: string[]): viewEvents.ViewLinesInsertedEvent | null { + public onModelLinesInserted(versionId: number, fromLineNumber: number, _toLineNumber: number, lineBreaks: (LineBreakData | null)[]): viewEvents.ViewLinesInsertedEvent | null { if (versionId <= this._validModelVersionId) { // Here we check for versionId in case the lines were reconstructed in the meantime. // We don't want to apply stale change events on top of a newer read model state. @@ -411,10 +563,10 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let totalOutputLineCount = 0; let insertLines: ISplitLine[] = []; - let insertPrefixSumValues = new Uint32Array(text.length); + let insertPrefixSumValues: number[] = []; - for (let i = 0, len = text.length; i < len; i++) { - let line = createSplitLine(this.linePositionMapperFactory, text[i], this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, !isInHiddenArea); + for (let i = 0, len = lineBreaks.length; i < len; i++) { + let line = createSplitLine(lineBreaks[i], !isInHiddenArea); insertLines.push(line); let outputLineCount = line.getViewLineCount(); @@ -430,7 +582,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesInsertedEvent(outputFromLineNumber, outputFromLineNumber + totalOutputLineCount - 1); } - public onModelLineChanged(versionId: number, lineNumber: number, newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { + public onModelLineChanged(versionId: number, lineNumber: number, lineBreakData: LineBreakData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { if (versionId <= this._validModelVersionId) { // Here we check for versionId in case the lines were reconstructed in the meantime. // We don't want to apply stale change events on top of a newer read model state. @@ -441,7 +593,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { let oldOutputLineCount = this.lines[lineIndex].getViewLineCount(); let isVisible = this.lines[lineIndex].isVisible(); - let line = createSplitLine(this.linePositionMapperFactory, newText, this.tabSize, this.wrappingColumn, this.columnsForFullWidthChar, this.wrappingIndent, isVisible); + let line = createSplitLine(lineBreakData, isVisible); this.lines[lineIndex] = line; let newOutputLineCount = this.lines[lineIndex].getViewLineCount(); @@ -488,7 +640,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineCount(): number { - this._ensureValidState(); return this.prefixSumComputer.getTotalValue(); } @@ -496,22 +647,14 @@ export class SplitLinesCollection implements IViewModelLinesCollection { if (viewLineNumber < 1) { return 1; } - let viewLineCount = this.getViewLineCount(); + const viewLineCount = this.getViewLineCount(); if (viewLineNumber > viewLineCount) { return viewLineCount; } - return viewLineNumber; - } - - /** - * Gives a hint that a lot of requests are about to come in for these line numbers. - */ - public warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void { - this.prefixSumComputer.warmUpCache(viewStartLineNumber - 1, viewEndLineNumber - 1); + return viewLineNumber | 0; } public getActiveIndentGuide(viewLineNumber: number, minLineNumber: number, maxLineNumber: number): IActiveIndentGuideInfo { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); minLineNumber = this._toValidViewLineNumber(minLineNumber); maxLineNumber = this._toValidViewLineNumber(maxLineNumber); @@ -531,7 +674,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLinesIndentGuides(viewStartLineNumber: number, viewEndLineNumber: number): number[] { - this._ensureValidState(); viewStartLineNumber = this._toValidViewLineNumber(viewStartLineNumber); viewEndLineNumber = this._toValidViewLineNumber(viewEndLineNumber); @@ -602,7 +744,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineContent(viewLineNumber: number): string { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -612,7 +753,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineLength(viewLineNumber: number): number { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -622,7 +762,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineMinColumn(viewLineNumber: number): number { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -632,7 +771,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineMaxColumn(viewLineNumber: number): number { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -642,7 +780,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLineData(viewLineNumber: number): ViewLineData { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); let lineIndex = r.index; @@ -652,7 +789,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): ViewLineData[] { - this._ensureValidState(); viewStartLineNumber = this._toValidViewLineNumber(viewStartLineNumber); viewEndLineNumber = this._toValidViewLineNumber(viewEndLineNumber); @@ -691,7 +827,6 @@ export class SplitLinesCollection implements IViewModelLinesCollection { } public validateViewPosition(viewLineNumber: number, viewColumn: number, expectedModelPosition: Position): Position { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); @@ -719,8 +854,13 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return this.convertModelPositionToViewPosition(expectedModelPosition.lineNumber, expectedModelPosition.column); } + public validateViewRange(viewRange: Range, expectedModelRange: Range): Range { + const validViewStart = this.validateViewPosition(viewRange.startLineNumber, viewRange.startColumn, expectedModelRange.getStartPosition()); + const validViewEnd = this.validateViewPosition(viewRange.endLineNumber, viewRange.endColumn, expectedModelRange.getEndPosition()); + return new Range(validViewStart.lineNumber, validViewStart.column, validViewEnd.lineNumber, validViewEnd.column); + } + public convertViewPositionToModelPosition(viewLineNumber: number, viewColumn: number): Position { - this._ensureValidState(); viewLineNumber = this._toValidViewLineNumber(viewLineNumber); let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1); @@ -732,8 +872,13 @@ export class SplitLinesCollection implements IViewModelLinesCollection { return this.model.validatePosition(new Position(lineIndex + 1, inputColumn)); } + public convertViewRangeToModelRange(viewRange: Range): Range { + const start = this.convertViewPositionToModelPosition(viewRange.startLineNumber, viewRange.startColumn); + const end = this.convertViewPositionToModelPosition(viewRange.endLineNumber, viewRange.endColumn); + return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } + public convertModelPositionToViewPosition(_modelLineNumber: number, _modelColumn: number): Position { - this._ensureValidState(); const validPosition = this.model.validatePosition(new Position(_modelLineNumber, _modelColumn)); const inputLineNumber = validPosition.lineNumber; @@ -795,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) { @@ -898,6 +1043,10 @@ class VisibleIdentitySplitLine implements ISplitLine { return InvisibleIdentitySplitLine.INSTANCE; } + public getLineBreakData(): LineBreakData | null { + return null; + } + public getViewLineCount(): number { return 1; } @@ -926,6 +1075,7 @@ class VisibleIdentitySplitLine implements ISplitLine { false, 1, lineContent.length + 1, + 0, lineTokens.inflate() ); } @@ -968,6 +1118,10 @@ class InvisibleIdentitySplitLine implements ISplitLine { return VisibleIdentitySplitLine.INSTANCE; } + public getLineBreakData(): LineBreakData | null { + return null; + } + public getViewLineCount(): number { return 0; } @@ -1011,18 +1165,11 @@ class InvisibleIdentitySplitLine implements ISplitLine { export class SplitLine implements ISplitLine { - private readonly positionMapper: ILineMapping; - private readonly outputLineCount: number; - - private readonly wrappedIndent: string; - private readonly wrappedIndentLength: number; + private readonly _lineBreakData: LineBreakData; private _isVisible: boolean; - constructor(positionMapper: ILineMapping, isVisible: boolean) { - this.positionMapper = positionMapper; - this.wrappedIndent = this.positionMapper.getWrappedLinesIndent(); - this.wrappedIndentLength = this.wrappedIndent.length; - this.outputLineCount = this.positionMapper.getOutputLineCount(); + constructor(lineBreakData: LineBreakData, isVisible: boolean) { + this._lineBreakData = lineBreakData; this._isVisible = isVisible; } @@ -1035,22 +1182,26 @@ export class SplitLine implements ISplitLine { return this; } + public getLineBreakData(): LineBreakData | null { + return this._lineBreakData; + } + public getViewLineCount(): number { if (!this._isVisible) { return 0; } - return this.outputLineCount; + return this._lineBreakData.breakOffsets.length; } private getInputStartOffsetOfOutputLineIndex(outputLineIndex: number): number { - return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex, 0); + return LineBreakData.getInputOffsetOfOutputPosition(this._lineBreakData.breakOffsets, outputLineIndex, 0); } private getInputEndOffsetOfOutputLineIndex(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): number { - if (outputLineIndex + 1 === this.outputLineCount) { + if (outputLineIndex + 1 === this._lineBreakData.breakOffsets.length) { return model.getLineMaxColumn(modelLineNumber) - 1; } - return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex + 1, 0); + return LineBreakData.getInputOffsetOfOutputPosition(this._lineBreakData.breakOffsets, outputLineIndex + 1, 0); } public getViewLineContent(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number): string { @@ -1067,7 +1218,7 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - r = this.wrappedIndent + r; + r = spaces(this._lineBreakData.wrappedTextIndentLength) + r; } return r; @@ -1082,7 +1233,7 @@ export class SplitLine implements ISplitLine { let r = endOffset - startOffset; if (outputLineIndex > 0) { - r = this.wrappedIndent.length + r; + r = this._lineBreakData.wrappedTextIndentLength + r; } return r; @@ -1093,7 +1244,7 @@ export class SplitLine implements ISplitLine { throw new Error('Not supported'); } if (outputLineIndex > 0) { - return this.wrappedIndentLength + 1; + return this._lineBreakData.wrappedTextIndentLength + 1; } return 1; } @@ -1121,25 +1272,28 @@ export class SplitLine implements ISplitLine { }); if (outputLineIndex > 0) { - lineContent = this.wrappedIndent + lineContent; + lineContent = spaces(this._lineBreakData.wrappedTextIndentLength) + lineContent; } - let minColumn = (outputLineIndex > 0 ? this.wrappedIndentLength + 1 : 1); + let minColumn = (outputLineIndex > 0 ? this._lineBreakData.wrappedTextIndentLength + 1 : 1); let maxColumn = lineContent.length + 1; let continuesWithWrappedLine = (outputLineIndex + 1 < this.getViewLineCount()); let deltaStartIndex = 0; if (outputLineIndex > 0) { - deltaStartIndex = this.wrappedIndentLength; + deltaStartIndex = this._lineBreakData.wrappedTextIndentLength; } let lineTokens = model.getLineTokens(modelLineNumber); + const startVisibleColumn = (outputLineIndex === 0 ? 0 : this._lineBreakData.breakOffsetsVisibleColumn[outputLineIndex - 1]); + return new ViewLineData( lineContent, continuesWithWrappedLine, minColumn, maxColumn, + startVisibleColumn, lineTokens.sliceAndInflate(startOffset, endOffset, deltaStartIndex) ); } @@ -1165,25 +1319,25 @@ export class SplitLine implements ISplitLine { } let adjustedColumn = outputColumn - 1; if (outputLineIndex > 0) { - if (adjustedColumn < this.wrappedIndentLength) { + if (adjustedColumn < this._lineBreakData.wrappedTextIndentLength) { adjustedColumn = 0; } else { - adjustedColumn -= this.wrappedIndentLength; + adjustedColumn -= this._lineBreakData.wrappedTextIndentLength; } } - return this.positionMapper.getInputOffsetOfOutputPosition(outputLineIndex, adjustedColumn) + 1; + return LineBreakData.getInputOffsetOfOutputPosition(this._lineBreakData.breakOffsets, outputLineIndex, adjustedColumn) + 1; } public getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position { if (!this._isVisible) { throw new Error('Not supported'); } - let r = this.positionMapper.getOutputPositionOfInputOffset(inputColumn - 1); + let r = LineBreakData.getOutputPositionOfInputOffset(this._lineBreakData.breakOffsets, inputColumn - 1); let outputLineIndex = r.outputLineIndex; let outputColumn = r.outputOffset + 1; if (outputLineIndex > 0) { - outputColumn += this.wrappedIndentLength; + outputColumn += this._lineBreakData.wrappedTextIndentLength; } // console.log('in -> out ' + deltaLineNumber + ',' + inputColumn + ' ===> ' + (deltaLineNumber+outputLineIndex) + ',' + outputColumn); @@ -1194,21 +1348,33 @@ export class SplitLine implements ISplitLine { if (!this._isVisible) { throw new Error('Not supported'); } - const r = this.positionMapper.getOutputPositionOfInputOffset(inputColumn - 1); + const r = LineBreakData.getOutputPositionOfInputOffset(this._lineBreakData.breakOffsets, inputColumn - 1); return (deltaLineNumber + r.outputLineIndex); } } -function createSplitLine(linePositionMapperFactory: ILineMapperFactory, text: string, tabSize: number, wrappingColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, isVisible: boolean): ISplitLine { - let positionMapper = linePositionMapperFactory.createLineMapping(text, tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); - if (positionMapper === null) { +let _spaces: string[] = ['']; +function spaces(count: number): string { + if (count >= _spaces.length) { + for (let i = 1; i <= count; i++) { + _spaces[i] = _makeSpaces(i); + } + } + return _spaces[count]; +} +function _makeSpaces(count: number): string { + return new Array(count + 1).join(' '); +} + +function createSplitLine(lineBreakData: LineBreakData | null, isVisible: boolean): ISplitLine { + if (lineBreakData === null) { // No mapping needed if (isVisible) { return VisibleIdentitySplitLine.INSTANCE; } return InvisibleIdentitySplitLine.INSTANCE; } else { - return new SplitLine(positionMapper, isVisible); + return new SplitLine(lineBreakData, isVisible); } } @@ -1294,10 +1460,22 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return false; } - public setWrappingSettings(_wrappingIndent: WrappingIndent, _wrappingColumn: number, _columnsForFullWidthChar: number): boolean { + public setWrappingSettings(_fontInfo: FontInfo, _wrappingStrategy: 'simple' | 'advanced', _wrappingColumn: number, _wrappingIndent: WrappingIndent): boolean { return false; } + public createLineBreaksComputer(): ILineBreaksComputer { + let result: null[] = []; + return { + addRequest: (lineText: string, previousLineBreakData: LineBreakData | null) => { + result.push(null); + }, + finalize: () => { + return result; + } + }; + } + public onModelFlushed(): void { } @@ -1305,11 +1483,11 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return new viewEvents.ViewLinesDeletedEvent(fromLineNumber, toLineNumber); } - public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, _text: string[]): viewEvents.ViewLinesInsertedEvent | null { + public onModelLinesInserted(_versionId: number, fromLineNumber: number, toLineNumber: number, lineBreaks: (LineBreakData | null)[]): viewEvents.ViewLinesInsertedEvent | null { return new viewEvents.ViewLinesInsertedEvent(fromLineNumber, toLineNumber); } - public onModelLineChanged(_versionId: number, lineNumber: number, _newText: string): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { + public onModelLineChanged(_versionId: number, lineNumber: number, lineBreakData: LineBreakData | null): [boolean, viewEvents.ViewLinesChangedEvent | null, viewEvents.ViewLinesInsertedEvent | null, viewEvents.ViewLinesDeletedEvent | null] { return [false, new viewEvents.ViewLinesChangedEvent(lineNumber, lineNumber), null, null]; } @@ -1320,9 +1498,6 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { return this.model.getLineCount(); } - public warmUpLookupCache(_viewStartLineNumber: number, _viewEndLineNumber: number): void { - } - public getActiveIndentGuide(viewLineNumber: number, _minLineNumber: number, _maxLineNumber: number): IActiveIndentGuideInfo { return { startLineNumber: viewLineNumber, @@ -1364,6 +1539,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { false, 1, lineContent.length + 1, + 0, lineTokens.inflate() ); } @@ -1385,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 e421f5ffde294..aad7a0b495726 100644 --- a/src/vs/editor/common/viewModel/viewEventHandler.ts +++ b/src/vs/editor/common/viewModel/viewEventHandler.ts @@ -36,6 +36,7 @@ export class ViewEventHandler extends Disposable { public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { return false; } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { return false; } @@ -69,6 +70,9 @@ export class ViewEventHandler extends Disposable { public onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { return false; } + public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { + return false; + } public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean { return false; } @@ -78,9 +82,6 @@ export class ViewEventHandler extends Disposable { public onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { return false; } - public onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { - return false; - } // --- end event handlers @@ -171,20 +172,20 @@ export class ViewEventHandler extends Disposable { } break; - case viewEvents.ViewEventType.ViewTokensColorsChanged: - if (this.onTokensColorsChanged(e)) { + case viewEvents.ViewEventType.ViewThemeChanged: + if (this.onThemeChanged(e)) { shouldRender = true; } break; - case viewEvents.ViewEventType.ViewZonesChanged: - if (this.onZonesChanged(e)) { + case viewEvents.ViewEventType.ViewTokensColorsChanged: + if (this.onTokensColorsChanged(e)) { shouldRender = true; } break; - case viewEvents.ViewEventType.ViewThemeChanged: - if (this.onThemeChanged(e)) { + case viewEvents.ViewEventType.ViewZonesChanged: + if (this.onZonesChanged(e)) { shouldRender = true; } break; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 0f16d0a84a299..91ab8513c4c63 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 } from 'vs/editor/common/viewLayout/whitespaceComputer'; -import { ITheme } from 'vs/platform/theme/common/themeService'; +import { IEditorWhitespace, IWhitespaceChangeAccessor } from 'vs/editor/common/viewLayout/linesLayout'; +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; @@ -41,9 +43,7 @@ export class Viewport { export interface IViewLayout { - readonly scrollable: Scrollable; - - onMaxLineWidthChanged(width: number): void; + getScrollable(): Scrollable; 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,30 +65,10 @@ export interface IViewLayout { getVerticalOffsetForLineNumber(lineNumber: number): number; getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null; - // --------------- Begin vertical whitespace management - - /** - * Reserve rendering space. - * @return an identifier that can be later used to remove or change the whitespace. - */ - addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): string; - /** - * Change the properties of a whitespace. - */ - changeWhitespace(id: string, newAfterLineNumber: number, newHeight: number): boolean; - /** - * Remove rendering space - */ - removeWhitespace(id: string): boolean; /** * 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 { @@ -107,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; @@ -129,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; @@ -139,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; @@ -150,8 +133,40 @@ export interface IViewModel { deduceModelPositionRelativeToViewPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position; getEOL(): string; - getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[]; - getHTMLToCopy(ranges: Range[], emptySelectionClipboard: boolean): string | null; + 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 { @@ -186,6 +201,10 @@ export class ViewLineData { * The maximum allowed column at this view line. */ public readonly maxColumn: number; + /** + * The visible column at the start of the line (after the fauxIndent). + */ + public readonly startVisibleColumn: number; /** * The tokens at this view line. */ @@ -196,12 +215,14 @@ export class ViewLineData { continuesWithWrappedLine: boolean, minColumn: number, maxColumn: number, + startVisibleColumn: number, tokens: IViewLineTokens ) { this.content = content; this.continuesWithWrappedLine = continuesWithWrappedLine; this.minColumn = minColumn; this.maxColumn = maxColumn; + this.startVisibleColumn = startVisibleColumn; this.tokens = tokens; } } @@ -243,6 +264,10 @@ export class ViewLineRenderingData { * The tab size for this view model. */ public readonly tabSize: number; + /** + * The visible column at the start of the line (after the fauxIndent) + */ + public readonly startVisibleColumn: number; constructor( minColumn: number, @@ -253,7 +278,8 @@ export class ViewLineRenderingData { mightContainNonBasicASCII: boolean, tokens: IViewLineTokens, inlineDecorations: InlineDecoration[], - tabSize: number + tabSize: number, + startVisibleColumn: number ) { this.minColumn = minColumn; this.maxColumn = maxColumn; @@ -266,6 +292,7 @@ export class ViewLineRenderingData { this.tokens = tokens; this.inlineDecorations = inlineDecorations; this.tabSize = tabSize; + this.startVisibleColumn = startVisibleColumn; } public static isBasicASCII(lineContent: string, mightContainNonBasicASCII: boolean): boolean { diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts index b74baa8dd1cb7..c30da7ecdf431 100644 --- a/src/vs/editor/common/viewModel/viewModelDecorations.ts +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -10,7 +10,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDecoration, ITextModel } from 'vs/editor/common/model'; import { IViewModelLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ICoordinatesConverter, InlineDecoration, InlineDecorationType, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; export interface IDecorationsViewportData { /** @@ -104,7 +104,7 @@ export class ViewModelDecorations implements IDisposable { } private _getDecorationsViewportData(viewportRange: Range): IDecorationsViewportData { - const modelDecorations = this._linesCollection.getDecorationsInRange(viewportRange, this.editorId, this.configuration.options.get(EditorOption.readOnly)); + const modelDecorations = this._linesCollection.getDecorationsInRange(viewportRange, this.editorId, filterValidationDecorations(this.configuration.options)); const startLineNumber = viewportRange.startLineNumber; const endLineNumber = viewportRange.endLineNumber; diff --git a/src/vs/editor/common/viewModel/viewModelEventDispatcher.ts b/src/vs/editor/common/viewModel/viewModelEventDispatcher.ts new file mode 100644 index 0000000000000..0691b542cd103 --- /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 e08af64ebf9aa..f7f130f086690 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 } from 'vs/editor/common/config/editorOptions'; +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 * as editorCommon 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'; @@ -18,119 +20,149 @@ import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout'; -import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper'; -import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; +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: editorCommon.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; - - constructor(editorId: number, configuration: editorCommon.IConfiguration, model: ITextModel, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { + private readonly _cursor: Cursor; + private readonly _decorations: ViewModelDecorations; + + constructor( + editorId: number, + configuration: IConfiguration, + model: ITextModel, + domLineBreaksComputerFactory: ILineBreaksComputerFactory, + monospaceLineBreaksComputerFactory: ILineBreaksComputerFactory, + scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable + ) { 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 wrappingInfo = options.get(EditorOption.wrappingInfo); + const options = this._configuration.options; const fontInfo = options.get(EditorOption.fontInfo); - const wordWrapBreakAfterCharacters = options.get(EditorOption.wordWrapBreakAfterCharacters); - const wordWrapBreakBeforeCharacters = options.get(EditorOption.wordWrapBreakBeforeCharacters); - const wordWrapBreakObtrusiveCharacters = options.get(EditorOption.wordWrapBreakObtrusiveCharacters); + const wrappingStrategy = options.get(EditorOption.wrappingStrategy); + const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); - let hardWrappingLineMapperFactory = new CharacterHardWrappingLineMapperFactory( - wordWrapBreakBeforeCharacters, - wordWrapBreakAfterCharacters, - wordWrapBreakObtrusiveCharacters - ); - - this.lines = new SplitLinesCollection( + this._lines = new SplitLinesCollection( this.model, - hardWrappingLineMapperFactory, + domLineBreaksComputerFactory, + monospaceLineBreaksComputerFactory, + fontInfo, this.model.getOptions().tabSize, + wrappingStrategy, wrappingInfo.wrappingColumn, - fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth, wrappingIndent ); } - this.coordinatesConverter = this.lines.createCoordinatesConverter(); + this.coordinatesConverter = this._lines.createCoordinatesConverter(); + + this._cursor = this._register(new Cursor(model, this, this.coordinatesConverter, this.cursorConfig)); - this.viewLayout = this._register(new ViewLayout(this.configuration, this.getLineCount(), scheduleAtNextAnimationFrame)); + 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) => { + 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 { @@ -141,50 +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)); + } + + public onDidColorThemeChange(): void { + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent()); } - private _onConfigurationChanged(eventsCollector: viewEvents.ViewEventsCollector, e: ConfigurationChangedEvent): void { + 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 wrappingInfo = options.get(EditorOption.wrappingInfo); + const options = this._configuration.options; const fontInfo = options.get(EditorOption.fontInfo); + const wrappingStrategy = options.get(EditorOption.wrappingStrategy); + const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); - if (this.lines.setWrappingSettings(wrappingIndent, wrappingInfo.wrappingColumn, fontInfo.typicalFullwidthCharacterWidth / fontInfo.typicalHalfwidthCharacterWidth)) { - 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); } } @@ -192,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; @@ -200,48 +248,72 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const changes = e.changes; const versionId = e.versionId; - for (let j = 0, lenJ = changes.length; j < lenJ; j++) { - const change = changes[j]; + // Do a first pass to compute line mappings, and a second pass to actually interpret them + const lineBreaksComputer = this._lines.createLineBreaksComputer(); + for (const change of changes) { + switch (change.changeType) { + case textModelEvents.RawContentChangedType.LinesInserted: { + for (const line of change.detail) { + lineBreaksComputer.addRequest(line, null); + } + break; + } + case textModelEvents.RawContentChangedType.LineChanged: { + lineBreaksComputer.addRequest(change.detail, null); + break; + } + } + } + const lineBreaks = lineBreaksComputer.finalize(); + let lineBreaksOffset = 0; + + for (const change of changes) { 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; break; } case textModelEvents.RawContentChangedType.LinesInserted: { - const linesInsertedEvent = this.lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, change.detail); + const insertedLineBreaks = lineBreaks.slice(lineBreaksOffset, lineBreaksOffset + change.detail.length); + lineBreaksOffset += change.detail.length; + + 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; break; } case textModelEvents.RawContentChangedType.LineChanged: { - const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this.lines.onModelLineChanged(versionId, change.lineNumber, change.detail); + const changedLineBreakData = lineBreaks[lineBreaksOffset]; + lineBreaksOffset++; + + 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; @@ -252,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) => { @@ -290,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(); @@ -303,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]; @@ -422,7 +520,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel ); } - public saveState(): editorCommon.IViewState { + public saveState(): IViewState { const compatViewState = this.viewLayout.saveState(); const scrollTop = compatViewState.scrollTop; @@ -437,7 +535,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel }; } - public reduceRestoreState(state: editorCommon.IViewState): { scrollLeft: number; scrollTop: number; } { + public reduceRestoreState(state: IViewState): { scrollLeft: number; scrollTop: number; } { if (typeof state.firstPosition === 'undefined') { // This is a view state serialized by an older version return this._reduceRestoreStateCompatibility(state); @@ -452,7 +550,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel }; } - private _reduceRestoreStateCompatibility(state: editorCommon.IViewState): { scrollLeft: number; scrollTop: number; } { + private _reduceRestoreStateCompatibility(state: IViewState): { scrollLeft: number; scrollTop: number; } { return { scrollLeft: state.scrollLeft, scrollTop: state.scrollTopWithoutViewZones! @@ -463,50 +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.lines.warmUpLookupCache(startLineNumber, endLineNumber); - - 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 { @@ -526,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( @@ -546,24 +642,25 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel mightContainNonBasicASCII, lineData.tokens, inlineDecorations, - tabSize + tabSize, + lineData.startVisibleColumn ); } 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, this.configuration.options.get(EditorOption.readOnly), theme); + public getAllOverviewRulerDecorations(theme: EditorTheme): IOverviewRulerDecorations { + return this._lines.getAllOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options), theme); } public invalidateOverviewRulerColorCache(): void { @@ -623,15 +720,15 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return this.model.getEOL(); } - public getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[] { + public getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[] { const newLineCharacter = forceCRLF ? '\r\n' : this.model.getEOL(); - ranges = ranges.slice(0); - ranges.sort(Range.compareRangesUsingStarts); + modelRanges = modelRanges.slice(0); + modelRanges.sort(Range.compareRangesUsingStarts); let hasEmptyRange = false; let hasNonEmptyRange = false; - for (const range of ranges) { + for (const range of modelRanges) { if (range.isEmpty()) { hasEmptyRange = true; } else { @@ -645,10 +742,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return ''; } - const modelLineNumbers = ranges.map((r) => { - const viewLineStart = new Position(r.startLineNumber, 1); - return this.coordinatesConverter.convertViewPositionToModelPosition(viewLineStart).lineNumber; - }); + const modelLineNumbers = modelRanges.map((r) => r.startLineNumber); let result = ''; for (let i = 0; i < modelLineNumbers.length; i++) { @@ -664,14 +758,14 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel // mixed empty selections and non-empty selections let result: string[] = []; let prevModelLineNumber = 0; - for (const range of ranges) { - const modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber; - if (range.isEmpty()) { + for (const modelRange of modelRanges) { + const modelLineNumber = modelRange.startLineNumber; + if (modelRange.isEmpty()) { if (modelLineNumber !== prevModelLineNumber) { result.push(this.model.getLineContent(modelLineNumber)); } } else { - result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); + result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); } prevModelLineNumber = modelLineNumber; } @@ -679,51 +773,55 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } let result: string[] = []; - for (const range of ranges) { - if (!range.isEmpty()) { - result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); + for (const modelRange of modelRanges) { + if (!modelRange.isEmpty()) { + result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); } } return result.length === 1 ? result[0] : result; } - public getHTMLToCopy(viewRanges: Range[], emptySelectionClipboard: boolean): string | null { - if (this.model.getLanguageIdentifier().id === LanguageId.PlainText) { + public getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null { + const languageId = this.model.getLanguageIdentifier(); + if (languageId.id === LanguageId.PlainText) { return null; } - if (viewRanges.length !== 1) { + if (modelRanges.length !== 1) { // no multiple selection support at this time return null; } - let range = this.coordinatesConverter.convertViewRangeToModelRange(viewRanges[0]); + let range = modelRanges[0]; if (range.isEmpty()) { if (!emptySelectionClipboard) { // nothing to copy return null; } - let lineNumber = range.startLineNumber; + const lineNumber = range.startLineNumber; 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}`; - return ( - `
    ` - + this._getHTMLToCopy(range, colorMap) - + '
    ' - ); + return { + mode: languageId.language, + html: ( + `
    ` + + this._getHTMLToCopy(range, colorMap) + + '
    ' + ) + }; } private _getHTMLToCopy(modelRange: Range, colorMap: string[]): string { @@ -762,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/editor/contrib/anchorSelect/anchorSelect.css b/src/vs/editor/contrib/anchorSelect/anchorSelect.css new file mode 100644 index 0000000000000..46ea76d20117c --- /dev/null +++ b/src/vs/editor/contrib/anchorSelect/anchorSelect.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +.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 0000000000000..80011cf590951 --- /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/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts index ac8db2f732549..5e150312ce66f 100644 --- a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts @@ -13,7 +13,7 @@ import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorCon 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 * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -103,7 +103,7 @@ class BracketsData { } } -export class BracketMatchingController extends Disposable implements editorCommon.IEditorContribution { +export class BracketMatchingController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.bracketMatchingController'; public static get(editor: ICodeEditor): BracketMatchingController { @@ -116,7 +116,7 @@ export class BracketMatchingController extends Disposable implements editorCommo private _lastVersionId: number; private _decorations: string[]; private readonly _updateBracketsSoon: RunOnceScheduler; - private _matchBrackets: boolean; + private _matchBrackets: 'never' | 'near' | 'always'; constructor( editor: ICodeEditor @@ -132,7 +132,7 @@ export class BracketMatchingController extends Disposable implements editorCommo this._updateBracketsSoon.schedule(); this._register(editor.onDidChangeCursorPosition((e) => { - if (!this._matchBrackets) { + if (this._matchBrackets === 'never') { // Early exit if nothing needs to be done! // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) return; @@ -153,12 +153,13 @@ export class BracketMatchingController extends Disposable implements editorCommo this._updateBracketsSoon.schedule(); })); this._register(editor.onDidChangeConfiguration((e) => { - this._matchBrackets = this._editor.getOption(EditorOption.matchBrackets); - if (!this._matchBrackets && this._decorations.length > 0) { - // Remove existing decorations if bracket matching is off + if (e.hasChanged(EditorOption.matchBrackets)) { + this._matchBrackets = this._editor.getOption(EditorOption.matchBrackets); this._decorations = this._editor.deltaDecorations(this._decorations, []); + this._lastBracketsData = []; + this._lastVersionId = 0; + this._updateBracketsSoon.schedule(); } - this._updateBracketsSoon.schedule(); })); } @@ -262,7 +263,7 @@ export class BracketMatchingController extends Disposable implements editorCommo }); private _updateBrackets(): void { - if (!this._matchBrackets) { + if (this._matchBrackets === 'never') { return; } this._recomputeBrackets(); @@ -332,8 +333,8 @@ export class BracketMatchingController extends Disposable implements editorCommo } else { let brackets = model.matchBracket(position); let options = BracketMatchingController._DECORATION_OPTIONS_WITH_OVERVIEW_RULER; - if (!brackets) { - brackets = model.findEnclosingBrackets(position); + if (!brackets && this._matchBrackets === 'always') { + brackets = model.findEnclosingBrackets(position, 20 /* give at most 20ms to compute */); options = BracketMatchingController._DECORATION_OPTIONS_WITHOUT_OVERVIEW_RULER; } newData[newDataLen++] = new BracketsData(position, brackets, options); diff --git a/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts b/src/vs/editor/contrib/bracketMatching/test/bracketMatching.test.ts index 87886115932b1..0a11a1a5f9985 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 3b715b871e733..abd6ab267fdc3 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 0ce4feb61749f..81db0fd6217b0 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) { + const lineNumber = this._selection.startLineNumber; + const startColumn = this._selection.startColumn; + const endColumn = this._selection.endColumn; + if (this._isMovingLeft && startColumn === 1) { return; - } else if (!this._isMovingLeft && s.endColumn === model.getLineMaxColumn(s.startLineNumber)) { + } + 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.css b/src/vs/editor/contrib/clipboard/clipboard.css deleted file mode 100644 index 7f14a01f05754..0000000000000 --- a/src/vs/editor/contrib/clipboard/clipboard.css +++ /dev/null @@ -1,8 +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 .action-label.hover { - background-color: #EEE; -} \ No newline at end of file diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index df241dc8643c0..6860a7bb61088 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'))); - -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); - } +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); + +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, - menuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 1 - }, - menubarOpts: { - 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, + } : 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; + +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; - } - - super('copy', { - id: 'editor.action.clipboardCopyAction', - label: nls.localize('actions.clipboard.copyLabel', "Copy"), - alias: 'Copy', - precondition: undefined, - kbOpts: kbOpts, - menuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 2 - }, - menubarOpts: { - 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, + 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 - }; - // Do not bind paste keybindings in the browser, - // since browsers do that for us and it avoids security prompts - if (!platform.isNative) { - kbOpts = undefined; - } - - super('paste', { - id: 'editor.action.clipboardPasteAction', - label: nls.localize('actions.clipboard.pasteLabel', "Paste"), - alias: 'Paste', - precondition: EditorContextKeys.writable, - kbOpts: kbOpts, - menuOpts: { - group: CLIPBOARD_CONTEXT_MENU_GROUP, - order: 3 - }, - menubarOpts: { - menuId: MenuId.MenubarEditMenu, - group: '2_ccp', - title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), - order: 3 - } - }); - } -} - -class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { + } : 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; + +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); -} -if (supportsCopy) { - registerEditorAction(ExecCommandCopyAction); +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 (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 a58d88d1a8819..f54e722566e26 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -3,28 +3,44 @@ * 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'; import { URI } from 'vs/base/common/uri'; +import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ITextModel } from 'vs/editor/common/model'; -import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes'; +import * as modes from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { CodeActionFilter, CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './codeActionTrigger'; -import { TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; -import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle'; +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'; +export const sourceActionCommandId = 'editor.action.sourceAction'; +export const organizeImportsCommandId = 'editor.action.organizeImports'; +export const fixAllCommandId = 'editor.action.fixAll'; export interface CodeActionSet extends IDisposable { - readonly actions: readonly CodeAction[]; + readonly validActions: readonly modes.CodeAction[]; + readonly allActions: readonly modes.CodeAction[]; readonly hasAutoFix: boolean; + + readonly documentation: readonly modes.Command[]; } class ManagedCodeActionSet extends Disposable implements CodeActionSet { - private static codeActionsComparator(a: CodeAction, b: CodeAction): number { + private static codeActionsComparator(a: modes.CodeAction, b: modes.CodeAction): 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); @@ -38,63 +54,82 @@ class ManagedCodeActionSet extends Disposable implements CodeActionSet { } } - public readonly actions: readonly CodeAction[]; + public readonly validActions: readonly modes.CodeAction[]; + public readonly allActions: readonly modes.CodeAction[]; - public constructor(actions: readonly CodeAction[], disposables: DisposableStore) { + public constructor( + actions: readonly modes.CodeAction[], + public readonly documentation: readonly modes.Command[], + disposables: DisposableStore, + ) { super(); this._register(disposables); - this.actions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator); + this.allActions = mergeSort([...actions], ManagedCodeActionSet.codeActionsComparator); + this.validActions = this.allActions.filter(action => !action.disabled); } public get hasAutoFix() { - return this.actions.some(fix => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred); + return this.validActions.some(fix => !!fix.kind && CodeActionKind.QuickFix.contains(new CodeActionKind(fix.kind)) && !!fix.isPreferred); } } + +const emptyCodeActionsResponse = { actions: [] as modes.CodeAction[], documentation: undefined }; + export function getCodeActions( model: ITextModel, rangeOrSelection: Range | Selection, trigger: CodeActionTrigger, - token: CancellationToken + progress: IProgress, + token: CancellationToken, ): Promise { const filter = trigger.filter || {}; - const codeActionContext: CodeActionContext = { - only: filter.kind ? filter.kind.value : undefined, - trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic + const codeActionContext: modes.CodeActionContext = { + only: filter.include?.value, + trigger: trigger.type, }; const cts = new TextModelCancellationTokenSource(model, token); const providers = getCodeActionProviders(model, filter); const disposables = new DisposableStore(); - const promises = providers.map(provider => { - return Promise.resolve(provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token)).then(providedCodeActions => { - if (cts.token.isCancellationRequested || !providedCodeActions) { - return []; + const promises = providers.map(async provider => { + try { + progress.report(provider); + const providedCodeActions = await provider.provideCodeActions(model, rangeOrSelection, codeActionContext, cts.token); + if (providedCodeActions) { + disposables.add(providedCodeActions); + } + + if (cts.token.isCancellationRequested) { + return emptyCodeActionsResponse; } - disposables.add(providedCodeActions); - return providedCodeActions.actions.filter(action => action && filtersAction(filter, action)); - }, (err): CodeAction[] => { + + const filteredActions = (providedCodeActions?.actions || []).filter(action => action && filtersAction(filter, action)); + const documentation = getDocumentation(provider, filteredActions, filter.include); + return { actions: filteredActions, documentation }; + } catch (err) { if (isPromiseCanceledError(err)) { throw err; } - onUnexpectedExternalError(err); - return []; - }); + return emptyCodeActionsResponse; + } }); - const listener = CodeActionProviderRegistry.onDidChange(() => { - const newProviders = CodeActionProviderRegistry.all(model); + const listener = modes.CodeActionProviderRegistry.onDidChange(() => { + const newProviders = modes.CodeActionProviderRegistry.all(model); if (!equals(newProviders, providers)) { cts.cancel(); } }); - 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(); @@ -105,7 +140,7 @@ function getCodeActionProviders( model: ITextModel, filter: CodeActionFilter ) { - return CodeActionProviderRegistry.all(model) + return modes.CodeActionProviderRegistry.all(model) // Don't include providers that we know will not return code actions of interest .filter(provider => { if (!provider.providedCodeActionKinds) { @@ -116,7 +151,53 @@ function getCodeActionProviders( }); } -registerLanguageCommand('_executeCodeActionProvider', async function (accessor, args): Promise> { +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; if (!(resource instanceof URI)) { throw illegalArgument(); @@ -140,9 +221,10 @@ registerLanguageCommand('_executeCodeActionProvider', async function (accessor, const codeActionSet = await getCodeActions( model, validatedRangeOrSelection, - { type: 'manual', filter: { includeSourceActions: true, kind: kind && kind.value ? new CodeActionKind(kind.value) : undefined } }, + { 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.actions; + return codeActionSet.validActions; }); diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 302542ec1d393..699e05a469904 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -11,26 +11,25 @@ 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 } from 'vs/editor/common/modes'; -import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; +import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; +import { codeActionCommandId, 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'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CodeActionModel, CodeActionsState, SUPPORTED_CODE_ACTIONS } from './codeActionModel'; -import { CodeActionAutoApply, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActionTrigger'; +import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionFilter, CodeActionKind, CodeActionTrigger } from './types'; function contextKeyForSupportedActions(kind: CodeActionKind) { return ContextKeyExpr.regex( @@ -40,7 +39,6 @@ function contextKeyForSupportedActions(kind: CodeActionKind) { const argsSchema: IJSONSchema = { type: 'object', - required: ['kind'], defaultSnippets: [{ body: { kind: '' } }], properties: { 'kind': { @@ -83,10 +81,6 @@ export class QuickFixController extends Disposable implements IEditorContributio @IMarkerService markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, @IEditorProgressService progressService: IEditorProgressService, - @IContextMenuService contextMenuService: IContextMenuService, - @IKeybindingService keybindingService: IKeybindingService, - @ICommandService private readonly _commandService: ICommandService, - @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); @@ -102,11 +96,11 @@ export class QuickFixController extends Disposable implements IEditorContributio await this._applyCodeAction(action); } finally { if (retrigger) { - this._trigger({ type: 'auto', filter: {} }); + this._trigger({ type: CodeActionTriggerType.Auto, filter: {} }); } } } - }, contextMenuService, keybindingService)) + }, this._instantiationService)) ); } @@ -114,8 +108,8 @@ export class QuickFixController extends Disposable implements IEditorContributio this._ui.getValue().update(newState); } - public showCodeActions(actions: CodeActionSet, at: IAnchor | IPosition) { - return this._ui.getValue().showCodeActionList(actions, at); + public showCodeActions(trigger: CodeActionTrigger, actions: CodeActionSet, at: IAnchor | IPosition) { + return this._ui.getValue().showCodeActionList(trigger, actions, at, { includeDisabledActions: false }); } public manualTriggerAtCurrentPosition( @@ -129,7 +123,7 @@ export class QuickFixController extends Disposable implements IEditorContributio MessageController.get(this._editor).closeMessage(); const triggerPosition = this._editor.getPosition(); - this._trigger({ type: 'manual', filter, autoApply, context: { notAvailableMessage, position: triggerPosition } }); + this._trigger({ type: CodeActionTriggerType.Manual, filter, autoApply, context: { notAvailableMessage, position: triggerPosition } }); } private _trigger(trigger: CodeActionTrigger) { @@ -137,21 +131,41 @@ export class QuickFixController extends Disposable implements IEditorContributio } private _applyCodeAction(action: CodeAction): Promise { - return this._instantiationService.invokeFunction(applyCodeAction, action, this._bulkEditService, this._commandService, this._editor); + return this._instantiationService.invokeFunction(applyCodeAction, action, this._editor); } } export async function applyCodeAction( accessor: ServicesAccessor, action: CodeAction, - bulkEditService: IBulkEditService, - commandService: ICommandService, editor?: ICodeEditor, ): Promise { + const bulkEditService = accessor.get(IBulkEditService); + const commandService = accessor.get(ICommandService); + const telemetryService = accessor.get(ITelemetryService); const notificationService = accessor.get(INotificationService); + + type ApplyCodeActionEvent = { + codeActionTitle: string; + codeActionKind: string | undefined; + codeActionIsPreferred: boolean; + }; + type ApplyCodeEventClassification = { + codeActionTitle: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + codeActionKind: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + codeActionIsPreferred: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + + telemetryService.publicLog2('codeAction.applyCodeAction', { + codeActionTitle: action.title, + codeActionKind: action.kind, + codeActionIsPreferred: !!action.isPreferred, + }); + if (action.edit) { - await bulkEditService.apply(action.edit, { editor }); + await bulkEditService.apply(ResourceEdit.convert(action.edit), { editor, label: action.title }); } + if (action.command) { try { await commandService.executeCommand(action.command.id, ...(action.command.arguments || [])); @@ -161,7 +175,6 @@ export async function applyCodeAction( typeof message === 'string' ? message : nls.localize('applyCodeActionFailed', "An unknown error occurred while applying the code action")); - } } } @@ -213,72 +226,34 @@ export class QuickFixAction extends EditorAction { } } - -class CodeActionCommandArgs { - public static fromUser(arg: any, defaults: { kind: CodeActionKind, apply: CodeActionAutoApply }): CodeActionCommandArgs { - if (!arg || typeof arg !== 'object') { - return new CodeActionCommandArgs(defaults.kind, defaults.apply, false); - } - return new CodeActionCommandArgs( - CodeActionCommandArgs.getKindFromUser(arg, defaults.kind), - CodeActionCommandArgs.getApplyFromUser(arg, defaults.apply), - CodeActionCommandArgs.getPreferredUser(arg)); - } - - private static getApplyFromUser(arg: any, defaultAutoApply: CodeActionAutoApply) { - switch (typeof arg.apply === 'string' ? arg.apply.toLowerCase() : '') { - case 'first': return CodeActionAutoApply.First; - case 'never': return CodeActionAutoApply.Never; - case 'ifsingle': return CodeActionAutoApply.IfSingle; - default: return defaultAutoApply; - } - } - - private static getKindFromUser(arg: any, defaultKind: CodeActionKind) { - return typeof arg.kind === 'string' - ? new CodeActionKind(arg.kind) - : defaultKind; - } - - private static getPreferredUser(arg: any): boolean { - return typeof arg.preferred === 'boolean' - ? arg.preferred - : false; - } - - private constructor( - public readonly kind: CodeActionKind, - public readonly apply: CodeActionAutoApply, - public readonly preferred: boolean, - ) { } -} - export class CodeActionCommand extends EditorCommand { - static readonly Id = 'editor.action.codeAction'; - constructor() { super({ - id: CodeActionCommand.Id, + id: codeActionCommandId, precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), description: { - description: `Trigger a code action`, - args: [{ - name: 'args', - schema: argsSchema, - }] + description: 'Trigger a code action', + args: [{ name: 'args', schema: argsSchema, }] } }); } - public runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any) { - const args = CodeActionCommandArgs.fromUser(userArg, { + public runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any) { + const args = CodeActionCommandArgs.fromUser(userArgs, { kind: CodeActionKind.Empty, apply: CodeActionAutoApply.IfSingle, }); - return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), + return triggerCodeActionsForEditorSelection(editor, + typeof userArgs?.kind === 'string' + ? args.preferred + ? nls.localize('editor.action.codeAction.noneMessage.preferred.kind', "No preferred code actions for '{0}' available", userArgs.kind) + : nls.localize('editor.action.codeAction.noneMessage.kind', "No code actions for '{0}' available", userArgs.kind) + : args.preferred + ? nls.localize('editor.action.codeAction.noneMessage.preferred', "No preferred code actions available") + : nls.localize('editor.action.codeAction.noneMessage', "No code actions available"), { - kind: args.kind, + include: args.kind, includeSourceActions: true, onlyIncludePreferredActions: args.preferred, }, @@ -289,11 +264,9 @@ export class CodeActionCommand extends EditorCommand { export class RefactorAction extends EditorAction { - static readonly Id = 'editor.action.refactor'; - constructor() { super({ - id: RefactorAction.Id, + id: refactorCommandId, label: nls.localize('refactor.label', "Refactor..."), alias: 'Refactor...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), @@ -305,7 +278,7 @@ export class RefactorAction extends EditorAction { }, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 2, when: ContextKeyExpr.and( @@ -314,41 +287,41 @@ export class RefactorAction extends EditorAction { }, description: { description: 'Refactor...', - args: [{ - name: 'args', - schema: argsSchema - }] + args: [{ name: 'args', schema: argsSchema }] } }); } - public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any): void { - const args = CodeActionCommandArgs.fromUser(userArg, { + public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void { + const args = CodeActionCommandArgs.fromUser(userArgs, { kind: CodeActionKind.Refactor, apply: CodeActionAutoApply.Never }); return triggerCodeActionsForEditorSelection(editor, - nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), + typeof userArgs?.kind === 'string' + ? args.preferred + ? nls.localize('editor.action.refactor.noneMessage.preferred.kind', "No preferred refactorings for '{0}' available", userArgs.kind) + : nls.localize('editor.action.refactor.noneMessage.kind', "No refactorings for '{0}' available", userArgs.kind) + : args.preferred + ? nls.localize('editor.action.refactor.noneMessage.preferred', "No preferred refactorings available") + : nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), { - kind: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.Empty, + include: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.None, onlyIncludePreferredActions: args.preferred, }, args.apply); } } - export class SourceAction extends EditorAction { - static readonly Id = 'editor.action.sourceAction'; - constructor() { super({ - id: SourceAction.Id, + id: sourceActionCommandId, label: nls.localize('source.label', "Source Action..."), alias: 'Source Action...', precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 2.1, when: ContextKeyExpr.and( @@ -357,23 +330,26 @@ export class SourceAction extends EditorAction { }, description: { description: 'Source Action...', - args: [{ - name: 'args', - schema: argsSchema - }] + args: [{ name: 'args', schema: argsSchema }] } }); } - public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any): void { - const args = CodeActionCommandArgs.fromUser(userArg, { + public run(_accessor: ServicesAccessor, editor: ICodeEditor, userArgs: any): void { + const args = CodeActionCommandArgs.fromUser(userArgs, { kind: CodeActionKind.Source, apply: CodeActionAutoApply.Never }); return triggerCodeActionsForEditorSelection(editor, - nls.localize('editor.action.source.noneMessage', "No source actions available"), + typeof userArgs?.kind === 'string' + ? args.preferred + ? nls.localize('editor.action.source.noneMessage.preferred.kind', "No preferred source actions for '{0}' available", userArgs.kind) + : nls.localize('editor.action.source.noneMessage.kind', "No source actions for '{0}' available", userArgs.kind) + : args.preferred + ? nls.localize('editor.action.source.noneMessage.preferred', "No preferred source actions available") + : nls.localize('editor.action.source.noneMessage', "No source actions available"), { - kind: CodeActionKind.Source.contains(args.kind) ? args.kind : CodeActionKind.Empty, + include: CodeActionKind.Source.contains(args.kind) ? args.kind : CodeActionKind.None, includeSourceActions: true, onlyIncludePreferredActions: args.preferred, }, @@ -383,11 +359,9 @@ export class SourceAction extends EditorAction { export class OrganizeImportsAction extends EditorAction { - static readonly Id = 'editor.action.organizeImports'; - constructor() { super({ - id: OrganizeImportsAction.Id, + id: organizeImportsCommandId, label: nls.localize('organizeImports.label', "Organize Imports"), alias: 'Organize Imports', precondition: ContextKeyExpr.and( @@ -397,25 +371,23 @@ export class OrganizeImportsAction extends EditorAction { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_O, weight: KeybindingWeight.EditorContrib - } + }, }); } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.organize.noneMessage', "No organize imports action available"), - { kind: CodeActionKind.SourceOrganizeImports, includeSourceActions: true }, + { include: CodeActionKind.SourceOrganizeImports, includeSourceActions: true }, CodeActionAutoApply.IfSingle); } } export class FixAllAction extends EditorAction { - static readonly Id = 'editor.action.fixAll'; - constructor() { super({ - id: FixAllAction.Id, + id: fixAllCommandId, label: nls.localize('fixAll.label', "Fix All"), alias: 'Fix All', precondition: ContextKeyExpr.and( @@ -427,7 +399,7 @@ export class FixAllAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return triggerCodeActionsForEditorSelection(editor, nls.localize('fixAll.noneMessage', "No fix all action available"), - { kind: CodeActionKind.SourceFixAll, includeSourceActions: true }, + { include: CodeActionKind.SourceFixAll, includeSourceActions: true }, CodeActionAutoApply.IfSingle); } } @@ -459,7 +431,7 @@ export class AutoFixAction extends EditorAction { return triggerCodeActionsForEditorSelection(editor, nls.localize('editor.action.autoFix.noneMessage', "No auto fixes available"), { - kind: CodeActionKind.QuickFix, + include: CodeActionKind.QuickFix, onlyIncludePreferredActions: true }, CodeActionAutoApply.IfSingle); diff --git a/src/vs/editor/contrib/codeAction/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/codeActionMenu.ts new file mode 100644 index 0000000000000..0c9ef9ec71aa9 --- /dev/null +++ b/src/vs/editor/contrib/codeAction/codeActionMenu.ts @@ -0,0 +1,226 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getDomNodePagePosition } from 'vs/base/browser/dom'; +import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; +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'; +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, Command } from 'vs/editor/common/modes'; +import { codeActionCommandId, 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; +} + +interface ResolveCodeActionKeybinding { + readonly kind: CodeActionKind; + readonly preferred: boolean; + readonly resolvedKeybinding: ResolvedKeybinding; +} + +class CodeActionAction extends Action { + constructor( + public readonly action: CodeAction, + callback: () => Promise, + ) { + super(action.command ? action.command.id : action.title, action.title, undefined, !action.disabled, callback); + } +} + +export interface CodeActionShowOptions { + readonly includeDisabledActions: boolean; +} + +export class CodeActionMenu extends Disposable { + + private _visible: boolean = false; + private readonly _showingActions = this._register(new MutableDisposable()); + + private readonly _keybindingResolver: CodeActionKeybindingResolver; + + constructor( + private readonly _editor: ICodeEditor, + private readonly _delegate: CodeActionWidgetDelegate, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, + ) { + super(); + + this._keybindingResolver = new CodeActionKeybindingResolver({ + getKeybindings: () => keybindingService.getKeybindings() + }); + } + + get isVisible(): boolean { + return this._visible; + } + + public async show(trigger: CodeActionTrigger, codeActions: CodeActionSet, at: IAnchor | IPosition, options: CodeActionShowOptions): Promise { + const actionsToShow = options.includeDisabledActions ? codeActions.allActions : codeActions.validActions; + if (!actionsToShow.length) { + this._visible = false; + return; + } + + if (!this._editor.getDomNode()) { + // cancel when editor went off-dom + this._visible = false; + throw canceled(); + } + + this._visible = true; + this._showingActions.value = codeActions; + + 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: () => { + this._visible = false; + this._editor.focus(); + }, + autoSelectFirstItem: true, + getKeyBinding: action => action instanceof CodeActionAction ? resolver(action.action) : undefined, + }); + } + + private getMenuActions( + trigger: CodeActionTrigger, + actionsToShow: readonly CodeAction[], + documentation: readonly Command[] + ): IAction[] { + const toCodeActionAction = (action: CodeAction): CodeActionAction => new CodeActionAction(action, () => this._delegate.onSelectCodeAction(action)); + + 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) { + allDocumentation.push(...provider._getAdditionalMenuItems({ trigger: trigger.type, only: trigger.filter?.include?.value }, actionsToShow)); + } + } + } + + if (allDocumentation.length) { + result.push(new Separator(), ...allDocumentation.map(command => toCodeActionAction({ + title: command.title, + command: command, + }))); + } + + return result; + } + + private _toCoords(position: IPosition): { x: number, y: number } { + if (!this._editor.hasModel()) { + return { x: 0, y: 0 }; + } + this._editor.revealPosition(position, ScrollType.Immediate); + this._editor.render(); + + // Translate to absolute editor position + const cursorCoords = this._editor.getScrolledVisiblePosition(position); + const editorCoords = getDomNodePagePosition(this._editor.getDomNode()); + const x = editorCoords.left + cursorCoords.left; + const y = editorCoords.top + cursorCoords.top + cursorCoords.height; + + return { x, y }; + } +} + +export class CodeActionKeybindingResolver { + private static readonly codeActionCommands: readonly string[] = [ + refactorCommandId, + codeActionCommandId, + sourceActionCommandId, + organizeImportsCommandId, + fixAllCommandId + ]; + + constructor( + private readonly _keybindingProvider: { + getKeybindings(): readonly ResolvedKeybindingItem[], + }, + ) { } + + public getResolver(): (action: CodeAction) => ResolvedKeybinding | undefined { + // Lazy since we may not actually ever read the value + const allCodeActionBindings = new Lazy(() => + this._keybindingProvider.getKeybindings() + .filter(item => CodeActionKeybindingResolver.codeActionCommands.indexOf(item.command!) >= 0) + .filter(item => item.resolvedKeybinding) + .map((item): ResolveCodeActionKeybinding => { + // Special case these commands since they come built-in with VS Code and don't use 'commandArgs' + let commandArgs = item.commandArgs; + if (item.command === organizeImportsCommandId) { + commandArgs = { kind: CodeActionKind.SourceOrganizeImports.value }; + } else if (item.command === fixAllCommandId) { + commandArgs = { kind: CodeActionKind.SourceFixAll.value }; + } + + return { + resolvedKeybinding: item.resolvedKeybinding!, + ...CodeActionCommandArgs.fromUser(commandArgs, { + kind: CodeActionKind.None, + apply: CodeActionAutoApply.Never + }) + }; + })); + + return (action) => { + if (action.kind) { + const binding = this.bestKeybindingForCodeAction(action, allCodeActionBindings.getValue()); + return binding?.resolvedKeybinding; + } + return undefined; + }; + } + + private bestKeybindingForCodeAction( + action: CodeAction, + candidates: readonly ResolveCodeActionKeybinding[], + ): ResolveCodeActionKeybinding | undefined { + if (!action.kind) { + return undefined; + } + const kind = new CodeActionKind(action.kind); + + return candidates + .filter(candidate => candidate.kind.contains(kind)) + .filter(candidate => { + if (candidate.preferred) { + // If the candidate keybinding only applies to preferred actions, the this action must also be preferred + return action.isPreferred; + } + return true; + }) + .reduceRight((currentBest, candidate) => { + if (!currentBest) { + return candidate; + } + // Select the more specific binding + return currentBest.kind.contains(candidate.kind) ? candidate : currentBest; + }, undefined as ResolveCodeActionKeybinding | undefined); + } +} diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index 91bd478b18f82..4afe90c451eb3 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -11,12 +11,12 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; 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 { CodeActionProviderRegistry } from 'vs/editor/common/modes'; +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 './codeActionTrigger'; +import { CodeActionTrigger } from './types'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { isEqual } from 'vs/base/common/resources'; @@ -56,14 +56,14 @@ class CodeActionOracle extends Disposable { if (resources.some(resource => isEqual(resource, model.uri))) { this._autoTriggerTimer.cancelAndSet(() => { - this.trigger({ type: 'auto' }); + this.trigger({ type: CodeActionTriggerType.Auto }); }, this._delay); } } private _onCursorChange(): void { this._autoTriggerTimer.cancelAndSet(() => { - this.trigger({ type: 'auto' }); + this.trigger({ type: CodeActionTriggerType.Auto }); }, this._delay); } @@ -73,10 +73,12 @@ class CodeActionOracle extends Disposable { return undefined; } for (const marker of this._markerService.read({ resource: model.uri })) { - if (Range.intersectRanges(marker, selection)) { - return Range.lift(marker); + const markerRange = model.validateRange(marker); + if (Range.intersectRanges(markerRange, selection)) { + return Range.lift(markerRange); } } + return undefined; } @@ -86,7 +88,7 @@ class CodeActionOracle extends Disposable { } const model = this._editor.getModel(); const selection = this._editor.getSelection(); - if (selection.isEmpty() && trigger.type === 'auto') { + if (selection.isEmpty() && trigger.type === CodeActionTriggerType.Auto) { const { lineNumber, column } = selection.getPosition(); const line = model.getLineContent(lineNumber); if (line.length === 0) { @@ -140,7 +142,7 @@ export namespace CodeActionsState { Triggered, } - export const Empty = new class { readonly type = Type.Empty; }; + export const Empty = { type: Type.Empty } as const; export class Triggered { readonly type = Type.Triggered; @@ -211,15 +213,15 @@ export class CodeActionModel extends Disposable { return; } - const actions = createCancelablePromise(token => getCodeActions(model, trigger.selection, trigger.trigger, token)); - if (this._progressService && trigger.trigger.type === '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)); }, undefined); - this._codeActionOracle.value.trigger({ type: 'auto' }); + this._codeActionOracle.value.trigger({ type: CodeActionTriggerType.Auto }); } else { this._supportedCodeActions.reset(); } diff --git a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts deleted file mode 100644 index d0d0f4bb9f538..0000000000000 --- a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts +++ /dev/null @@ -1,98 +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 { startsWith } from 'vs/base/common/strings'; -import { CodeAction } from 'vs/editor/common/modes'; -import { Position } from 'vs/editor/common/core/position'; - -export class CodeActionKind { - private static readonly sep = '.'; - - public static readonly Empty = new CodeActionKind(''); - public static readonly QuickFix = new CodeActionKind('quickfix'); - public static readonly Refactor = new CodeActionKind('refactor'); - public static readonly Source = new CodeActionKind('source'); - public static readonly SourceOrganizeImports = new CodeActionKind('source.organizeImports'); - public static readonly SourceFixAll = new CodeActionKind('source.fixAll'); - - constructor( - public readonly value: string - ) { } - - public equals(other: CodeActionKind): boolean { - return this.value === other.value; - } - - public contains(other: CodeActionKind): boolean { - return this.equals(other) || this.value === '' || startsWith(other.value, this.value + CodeActionKind.sep); - } - - public intersects(other: CodeActionKind): boolean { - return this.contains(other) || other.contains(this); - } -} - -export const enum CodeActionAutoApply { - IfSingle = 'ifSingle', - First = 'first', - Never = 'never', -} - -export interface CodeActionFilter { - readonly kind?: CodeActionKind; - readonly includeSourceActions?: boolean; - readonly onlyIncludePreferredActions?: boolean; -} - -export function mayIncludeActionsOfKind(filter: CodeActionFilter, providedKind: CodeActionKind): boolean { - // A provided kind may be a subset or superset of our filtered kind. - if (filter.kind && !filter.kind.intersects(providedKind)) { - return false; - } - - // Don't return source actions unless they are explicitly requested - if (CodeActionKind.Source.contains(providedKind) && !filter.includeSourceActions) { - return false; - } - - return true; -} - - -export function filtersAction(filter: CodeActionFilter, action: CodeAction): boolean { - const actionKind = action.kind ? new CodeActionKind(action.kind) : undefined; - - // Filter out actions by kind - if (filter.kind) { - if (!actionKind || !filter.kind.contains(actionKind)) { - return false; - } - } - - // Don't return source actions unless they are explicitly requested - if (!filter.includeSourceActions) { - if (actionKind && CodeActionKind.Source.contains(actionKind)) { - return false; - } - } - - if (filter.onlyIncludePreferredActions) { - if (!action.isPreferred) { - return false; - } - } - - return true; -} - -export interface CodeActionTrigger { - readonly type: 'auto' | 'manual'; - readonly filter?: CodeActionFilter; - readonly autoApply?: CodeActionAutoApply; - readonly context?: { - readonly notAvailableMessage: string; - readonly position: Position; - }; -} diff --git a/src/vs/editor/contrib/codeAction/codeActionUi.ts b/src/vs/editor/contrib/codeAction/codeActionUi.ts index 67edd66106cb9..6419e5e42f608 100644 --- a/src/vs/editor/contrib/codeAction/codeActionUi.ts +++ b/src/vs/editor/contrib/codeAction/codeActionUi.ts @@ -3,25 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; 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 { CodeAction } from 'vs/editor/common/modes'; +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 { MessageController } from 'vs/editor/contrib/message/messageController'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { CodeActionMenu, CodeActionShowOptions } from './codeActionMenu'; import { CodeActionsState } from './codeActionModel'; -import { CodeActionAutoApply } from './codeActionTrigger'; -import { CodeActionWidget } from './codeActionWidget'; import { LightBulbWidget } from './lightBulbWidget'; -import { IPosition } from 'vs/editor/common/core/position'; -import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; -import { Lazy } from 'vs/base/common/lazy'; +import { CodeActionAutoApply, CodeActionTrigger } from './types'; export class CodeActionUi extends Disposable { - private readonly _codeActionWidget: Lazy; + private readonly _codeActionWidget: Lazy; private readonly _lightBulbWidget: Lazy; private readonly _activeCodeActions = this._register(new MutableDisposable()); @@ -30,15 +29,14 @@ export class CodeActionUi extends Disposable { quickFixActionId: string, preferredFixActionId: string, private readonly delegate: { - applyCodeAction: (action: CodeAction, regtriggerAfterApply: boolean) => void + applyCodeAction: (action: CodeAction, regtriggerAfterApply: boolean) => Promise }, - @IContextMenuService contextMenuService: IContextMenuService, - @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService, ) { super(); this._codeActionWidget = new Lazy(() => { - return this._register(new CodeActionWidget(this._editor, contextMenuService, { + return this._register(instantiationService.createInstance(CodeActionMenu, this._editor, { onSelectCodeAction: async (action) => { this.delegate.applyCodeAction(action, /* retrigger */ true); } @@ -46,17 +44,15 @@ export class CodeActionUi extends Disposable { }); this._lightBulbWidget = new Lazy(() => { - const widget = this._register(new LightBulbWidget(this._editor, quickFixActionId, preferredFixActionId, keybindingService)); - this._register(widget.onClick(this._handleLightBulbSelect, this)); + const widget = this._register(instantiationService.createInstance(LightBulbWidget, this._editor, quickFixActionId, preferredFixActionId)); + this._register(widget.onClick(e => this.showCodeActionList(e.trigger, e.actions, e, { includeDisabledActions: false }))); return widget; }); } public async update(newState: CodeActionsState.State): Promise { if (newState.type !== CodeActionsState.Type.Triggered) { - if (this._lightBulbWidget.hasValue()) { - this._lightBulbWidget.getValue().hide(); - } + this._lightBulbWidget.rawValue?.hide(); return; } @@ -68,31 +64,45 @@ export class CodeActionUi extends Disposable { return; } - this._lightBulbWidget.getValue().update(actions, newState.position); + this._lightBulbWidget.getValue().update(actions, newState.trigger, newState.position); - if (!actions.actions.length && newState.trigger.context) { - MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position); - this._activeCodeActions.value = actions; - return; - } + if (newState.trigger.type === CodeActionTriggerType.Manual) { + if (newState.trigger.filter?.include) { // Triggered for specific scope + // Check to see if we want to auto apply. - if (newState.trigger.type === 'manual') { - if (newState.trigger.filter && newState.trigger.filter.kind) { - // Triggered for specific scope - if (actions.actions.length > 0) { - // Apply if we only have one action or requested autoApply - if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && actions.actions.length === 1)) { - try { - await this.delegate.applyCodeAction(actions.actions[0], false); - } finally { - actions.dispose(); - } + const validActionToApply = this.tryGetValidActionToApply(newState.trigger, actions); + if (validActionToApply) { + try { + await this.delegate.applyCodeAction(validActionToApply, false); + } finally { + actions.dispose(); + } + return; + } + + // 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); + actions.dispose(); return; } } } + + const includeDisabledActions = !!newState.trigger.filter?.include; + if (newState.trigger.context) { + if (!actions.allActions.length || !includeDisabledActions && !actions.validActions.length) { + MessageController.get(this._editor).showMessage(newState.trigger.context.notAvailableMessage, newState.trigger.context.position); + this._activeCodeActions.value = actions; + actions.dispose(); + return; + } + } + this._activeCodeActions.value = actions; - this._codeActionWidget.getValue().show(actions, newState.position); + this._codeActionWidget.getValue().show(newState.trigger, actions, newState.position, { includeDisabledActions }); } else { // auto magically triggered if (this._codeActionWidget.getValue().isVisible) { @@ -104,11 +114,35 @@ export class CodeActionUi extends Disposable { } } - public async showCodeActionList(actions: CodeActionSet, at?: IAnchor | IPosition): Promise { - this._codeActionWidget.getValue().show(actions, at); + private getInvalidActionThatWouldHaveBeenApplied(trigger: CodeActionTrigger, actions: CodeActionSet): CodeAction | undefined { + if (!actions.allActions.length) { + return undefined; + } + + if ((trigger.autoApply === CodeActionAutoApply.First && actions.validActions.length === 0) + || (trigger.autoApply === CodeActionAutoApply.IfSingle && actions.allActions.length === 1) + ) { + return actions.allActions.find(action => action.disabled); + } + + return undefined; + } + + private tryGetValidActionToApply(trigger: CodeActionTrigger, actions: CodeActionSet): CodeAction | undefined { + if (!actions.validActions.length) { + return undefined; + } + + if ((trigger.autoApply === CodeActionAutoApply.First && actions.validActions.length > 0) + || (trigger.autoApply === CodeActionAutoApply.IfSingle && actions.validActions.length === 1) + ) { + return actions.validActions[0]; + } + + return undefined; } - private _handleLightBulbSelect(e: { x: number, y: number, actions: CodeActionSet }): void { - this._codeActionWidget.getValue().show(e.actions, e); + public async showCodeActionList(trigger: CodeActionTrigger, actions: CodeActionSet, at: IAnchor | IPosition, options: CodeActionShowOptions): Promise { + this._codeActionWidget.getValue().show(trigger, actions, at, options); } } diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts deleted file mode 100644 index cb5b1eb4b2b7c..0000000000000 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ /dev/null @@ -1,91 +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 { getDomNodePagePosition } from 'vs/base/browser/dom'; -import { Action } from 'vs/base/common/actions'; -import { canceled } from 'vs/base/common/errors'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { Position, IPosition } from 'vs/editor/common/core/position'; -import { ScrollType } from 'vs/editor/common/editorCommon'; -import { CodeAction } from 'vs/editor/common/modes'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; -import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; -import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; - -interface CodeActionWidgetDelegate { - onSelectCodeAction: (action: CodeAction) => Promise; -} - -export class CodeActionWidget extends Disposable { - - private _visible: boolean = false; - private readonly _showingActions = this._register(new MutableDisposable()); - - constructor( - private readonly _editor: ICodeEditor, - private readonly _contextMenuService: IContextMenuService, - private readonly _delegate: CodeActionWidgetDelegate, - ) { - super(); - } - - public async show(codeActions: CodeActionSet, at?: IAnchor | IPosition): Promise { - if (!codeActions.actions.length) { - this._visible = false; - return; - } - if (!this._editor.getDomNode()) { - // cancel when editor went off-dom - this._visible = false; - return Promise.reject(canceled()); - } - - this._visible = true; - const actions = codeActions.actions.map(action => this.codeActionToAction(action)); - - this._showingActions.value = codeActions; - this._contextMenuService.showContextMenu({ - getAnchor: () => { - if (Position.isIPosition(at)) { - at = this._toCoords(at); - } - return at || { x: 0, y: 0 }; - }, - getActions: () => actions, - onHide: () => { - this._visible = false; - this._editor.focus(); - }, - autoSelectFirstItem: true - }); - } - - private codeActionToAction(action: CodeAction): Action { - const id = action.command ? action.command.id : action.title; - const title = action.title; - return new Action(id, title, undefined, true, () => this._delegate.onSelectCodeAction(action)); - } - - get isVisible(): boolean { - return this._visible; - } - - private _toCoords(position: IPosition): { x: number, y: number } { - if (!this._editor.hasModel()) { - return { x: 0, y: 0 }; - } - this._editor.revealPosition(position, ScrollType.Immediate); - this._editor.render(); - - // Translate to absolute editor position - const cursorCoords = this._editor.getScrolledVisiblePosition(position); - const editorCoords = getDomNodePagePosition(this._editor.getDomNode()); - const x = editorCoords.left + cursorCoords.left; - const y = editorCoords.top + cursorCoords.top + cursorCoords.height; - - return { x, y }; - } -} diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 505f13244f951..5caf1cd6a6099 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -15,8 +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 { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { editorLightBulbForeground, editorLightBulbAutoFixForeground } 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 { @@ -25,13 +28,14 @@ namespace LightBulbState { Showing, } - export const Hidden = new class { readonly type = Type.Hidden; }; + export const Hidden = { type: Type.Hidden } as const; export class Showing { readonly type = Type.Showing; constructor( public readonly actions: CodeActionSet, + public readonly trigger: CodeActionTrigger, public readonly editorPosition: IPosition, public readonly widgetPosition: IContentWidgetPosition, ) { } @@ -47,7 +51,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { private readonly _domNode: HTMLDivElement; - private readonly _onClick = this._register(new Emitter<{ x: number; y: number; actions: CodeActionSet; }>()); + private readonly _onClick = this._register(new Emitter<{ x: number; y: number; actions: CodeActionSet; trigger: CodeActionTrigger }>()); public readonly onClick = this._onClick.event; private _state: LightBulbState.State = LightBulbState.Hidden; @@ -60,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); @@ -71,7 +75,9 @@ export class LightBulbWidget extends Disposable implements IContentWidget { this.hide(); } })); - this._register(dom.addStandardDisposableListener(this._domNode, 'mousedown', e => { + + Gesture.ignoreTarget(this._domNode); + this._register(dom.addStandardDisposableGenericMouseDownListner(this._domNode, e => { if (this.state.type !== LightBulbState.Type.Showing) { return; } @@ -92,7 +98,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget { this._onClick.fire({ x: e.posx, y: top + height + pad, - actions: this.state.actions + actions: this.state.actions, + trigger: this.state.trigger, }); })); this._register(dom.addDisposableListener(this._domNode, 'mouseenter', (e: MouseEvent) => { @@ -104,7 +111,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { // showings until mouse is released this.hide(); const monitor = new GlobalMouseMoveMonitor(); - monitor.startMonitoring(standardMouseMoveMerger, () => { }, () => { + monitor.startMonitoring(e.target, e.buttons, standardMouseMoveMerger, () => { }, () => { monitor.dispose(); }); })); @@ -115,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 { @@ -136,8 +143,8 @@ export class LightBulbWidget extends Disposable implements IContentWidget { return this._state.type === LightBulbState.Type.Showing ? this._state.widgetPosition : null; } - public update(actions: CodeActionSet, atPosition: IPosition) { - if (actions.actions.length <= 0) { + public update(actions: CodeActionSet, trigger: CodeActionTrigger, atPosition: IPosition) { + if (actions.validActions.length <= 0) { return this.hide(); } @@ -146,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); @@ -174,11 +182,10 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } } - this.state = new LightBulbState.Showing(actions, atPosition, { + this.state = new LightBulbState.Showing(actions, trigger, atPosition, { position: { lineNumber: effectiveLineNumber, column: 1 }, preference: LightBulbWidget._posPref }); - dom.toggleClass(this._domNode, 'codicon-lightbulb-autofix', actions.hasAutoFix); this._editor.layoutContentWidget(this); } @@ -191,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 + dom.removeClasses(this._domNode, Codicon.lightBulb.classNames); + dom.addClasses(this._domNode, Codicon.lightbulbAutofix.classNames); + const preferredKb = this._keybindingService.lookupKeybinding(this._preferredFixActionId); if (preferredKb) { this.title = nls.localize('prefferedQuickFixWithKb', "Show Fixes. Preferred Fix Available ({0})", preferredKb.getLabel()); @@ -203,6 +214,10 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } } + // update icon + dom.removeClasses(this._domNode, Codicon.lightbulbAutofix.classNames); + dom.addClasses(this._domNode, Codicon.lightBulb.classNames); + const kb = this._keybindingService.lookupKeybinding(this._quickFixActionId); if (kb) { this.title = nls.localize('quickFixWithKb', "Show Fixes ({0})", kb.getLabel()); @@ -216,14 +231,13 @@ export class LightBulbWidget extends Disposable implements IContentWidget { } } -registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { +registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { // Lightbulb Icon const editorLightBulbForegroundColor = theme.getColor(editorLightBulbForeground); if (editorLightBulbForegroundColor) { collector.addRule(` - .monaco-workbench .contentWidgets .codicon-lightbulb, - .monaco-workbench .markers-panel-container .codicon-lightbulb { + .monaco-editor .contentWidgets ${Codicon.lightBulb.cssSelector} { color: ${editorLightBulbForegroundColor}; }`); } @@ -232,8 +246,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const editorLightBulbAutoFixForegroundColor = theme.getColor(editorLightBulbAutoFixForeground); if (editorLightBulbAutoFixForegroundColor) { collector.addRule(` - .monaco-workbench .contentWidgets .codicon-lightbulb-autofix, - .monaco-workbench .markers-panel-container .codicon-lightbulb-autofix { + .monaco-editor .contentWidgets ${Codicon.lightbulbAutofix.cssSelector} { color: ${editorLightBulbAutoFixForegroundColor}; }`); } diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index 41517305e3340..85510ab2a01c3 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -9,9 +9,11 @@ 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 { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; +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 { @@ -69,7 +71,7 @@ suite('CodeAction', () => { bcd: { diagnostics: [], edit: new class implements modes.WorkspaceEdit { - edits!: modes.ResourceTextEdit[]; + edits!: modes.WorkspaceTextEdit[]; }, title: 'abc' } @@ -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); }); @@ -125,7 +127,7 @@ suite('CodeAction', () => { testData.tsLint.abc ]; - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: '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 { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: 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'); } { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: 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'); } { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: 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,7 +174,7 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: 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'); }); @@ -186,13 +188,72 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: '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'); } { - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: 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'); + } + }); + + test('getCodeActions should support filtering out some requested source code actions #84602', async function () { + const provider = staticCodeActionProvider( + { title: 'a', kind: CodeActionKind.Source.value }, + { title: 'b', kind: CodeActionKind.Source.append('test').value }, + { title: 'c', kind: 'c' } + ); + + 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: CodeActionKind.Source.append('test'), + excludes: [CodeActionKind.Source], + includeSourceActions: true, + } + }, Progress.None, CancellationToken.None); + assert.equal(actions.length, 1); + assert.strictEqual(actions[0].title, 'b'); + } + }); + + test('getCodeActions no invoke a provider that has been excluded #84602', async function () { + const baseType = CodeActionKind.Refactor; + const subType = CodeActionKind.Refactor.append('sub'); + + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', staticCodeActionProvider( + { title: 'a', kind: baseType.value } + ))); + + let didInvoke = false; + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', new class implements modes.CodeActionProvider { + + providedCodeActionKinds = [subType.value]; + + provideCodeActions(): modes.ProviderResult { + didInvoke = true; + return { + actions: [ + { title: 'x', kind: subType.value } + ], + dispose: () => { } + }; + } + })); + + { + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { + type: modes.CodeActionTriggerType.Auto, filter: { + include: baseType, + excludes: [subType], + } + }, Progress.None, CancellationToken.None); + assert.strictEqual(didInvoke, false); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); } @@ -211,12 +272,12 @@ suite('CodeAction', () => { disposables.add(modes.CodeActionProviderRegistry.register('fooLang', provider)); - const { actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { - type: 'auto', + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { + type: modes.CodeActionTriggerType.Auto, filter: { - kind: CodeActionKind.QuickFix + 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 new file mode 100644 index 0000000000000..a0092488f750f --- /dev/null +++ b/src/vs/editor/contrib/codeAction/test/codeActionKeybindingResolver.test.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ChordKeybinding, KeyCode, SimpleKeybinding } from 'vs/base/common/keyCodes'; +import { OperatingSystem } from 'vs/base/common/platform'; +import { refactorCommandId, organizeImportsCommandId } from 'vs/editor/contrib/codeAction/codeAction'; +import { CodeActionKind } from 'vs/editor/contrib/codeAction/types'; +import { CodeActionKeybindingResolver } from 'vs/editor/contrib/codeAction/codeActionMenu'; +import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; +import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; + +suite('CodeActionKeybindingResolver', () => { + const refactorKeybinding = createCodeActionKeybinding( + KeyCode.KEY_A, + refactorCommandId, + { kind: CodeActionKind.Refactor.value }); + + const refactorExtractKeybinding = createCodeActionKeybinding( + KeyCode.KEY_B, + refactorCommandId, + { kind: CodeActionKind.Refactor.append('extract').value }); + + const organizeImportsKeybinding = createCodeActionKeybinding( + KeyCode.KEY_C, + organizeImportsCommandId, + undefined); + + test('Should match refactor keybindings', async function () { + const resolver = new CodeActionKeybindingResolver({ + getKeybindings: (): readonly ResolvedKeybindingItem[] => { + return [refactorKeybinding]; + }, + }).getResolver(); + + assert.equal( + resolver({ title: '' }), + undefined); + + assert.equal( + resolver({ title: '', kind: CodeActionKind.Refactor.value }), + refactorKeybinding.resolvedKeybinding); + + assert.equal( + resolver({ title: '', kind: CodeActionKind.Refactor.append('extract').value }), + refactorKeybinding.resolvedKeybinding); + + assert.equal( + resolver({ title: '', kind: CodeActionKind.QuickFix.value }), + undefined); + }); + + test('Should prefer most specific keybinding', async function () { + const resolver = new CodeActionKeybindingResolver({ + getKeybindings: (): readonly ResolvedKeybindingItem[] => { + return [refactorKeybinding, refactorExtractKeybinding, organizeImportsKeybinding]; + }, + }).getResolver(); + + assert.equal( + resolver({ title: '', kind: CodeActionKind.Refactor.value }), + refactorKeybinding.resolvedKeybinding); + + assert.equal( + resolver({ title: '', kind: CodeActionKind.Refactor.append('extract').value }), + refactorExtractKeybinding.resolvedKeybinding); + }); + + test('Organize imports should still return a keybinding even though it does not have args', async function () { + const resolver = new CodeActionKeybindingResolver({ + getKeybindings: (): readonly ResolvedKeybindingItem[] => { + return [refactorKeybinding, refactorExtractKeybinding, organizeImportsKeybinding]; + }, + }).getResolver(); + + assert.equal( + resolver({ title: '', kind: CodeActionKind.SourceOrganizeImports.value }), + organizeImportsKeybinding.resolvedKeybinding); + }); +}); + +function createCodeActionKeybinding(keycode: KeyCode, command: string, commandArgs: any) { + return new ResolvedKeybindingItem( + new USLayoutResolvedKeybinding( + new ChordKeybinding([new SimpleKeybinding(false, true, false, false, keycode)]), + OperatingSystem.Linux), + command, + commandArgs, + undefined, + 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 c2ef449356292..5edcc7ae851cc 100644 --- a/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeActionModel.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { assertType } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Selection } from 'vs/editor/common/core/selection'; @@ -14,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 { @@ -37,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 }); }); @@ -55,13 +57,15 @@ suite('CodeActionModel', () => { const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { - assert.equal(e.trigger.type, 'auto'); + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + + assert.strictEqual(e.trigger.type, modes.CodeActionTriggerType.Auto); assert.ok(e.actions); e.actions.then(fixes => { model.dispose(); - assert.equal(fixes.actions.length, 1); + assert.equal(fixes.validActions.length, 1); done(); }, done); })); @@ -94,12 +98,14 @@ suite('CodeActionModel', () => { return new Promise((resolve, reject) => { const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { - assert.equal(e.trigger.type, 'auto'); + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + + assert.equal(e.trigger.type, modes.CodeActionTriggerType.Auto); assert.ok(e.actions); e.actions.then(fixes => { model.dispose(); - assert.equal(fixes.actions.length, 1); + assert.equal(fixes.validActions.length, 1); resolve(undefined); }, reject); })); @@ -130,8 +136,10 @@ suite('CodeActionModel', () => { await new Promise(resolve => { const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { - assert.equal(e.trigger.type, 'auto'); + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + + assert.equal(e.trigger.type, modes.CodeActionTriggerType.Auto); const selection = e.rangeOrSelection; assert.deepEqual(selection.selectionStartLineNumber, 1); assert.deepEqual(selection.selectionStartColumn, 1); @@ -153,8 +161,10 @@ suite('CodeActionModel', () => { let triggerCount = 0; const contextKeys = new MockContextKeyService(); const model = disposables.add(new CodeActionModel(editor, markerService, contextKeys, undefined)); - disposables.add(model.onDidChangeState((e: CodeActionsState.Triggered) => { - assert.equal(e.trigger.type, 'auto'); + disposables.add(model.onDidChangeState((e: CodeActionsState.State) => { + assertType(e.type === CodeActionsState.Type.Triggered); + + assert.equal(e.trigger.type, modes.CodeActionTriggerType.Auto); ++triggerCount; // give time for second trigger before completing test diff --git a/src/vs/editor/contrib/codeAction/types.ts b/src/vs/editor/contrib/codeAction/types.ts new file mode 100644 index 0000000000000..b8f8c5e741e84 --- /dev/null +++ b/src/vs/editor/contrib/codeAction/types.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 { startsWith } from 'vs/base/common/strings'; +import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes'; +import { Position } from 'vs/editor/common/core/position'; + +export class CodeActionKind { + private static readonly sep = '.'; + + public static readonly None = new CodeActionKind('@@none@@'); // Special code action that contains nothing + public static readonly Empty = new CodeActionKind(''); + public static readonly QuickFix = new CodeActionKind('quickfix'); + public static readonly Refactor = new CodeActionKind('refactor'); + public static readonly Source = new CodeActionKind('source'); + public static readonly SourceOrganizeImports = CodeActionKind.Source.append('organizeImports'); + public static readonly SourceFixAll = CodeActionKind.Source.append('fixAll'); + + constructor( + public readonly value: string + ) { } + + public equals(other: CodeActionKind): boolean { + return this.value === other.value; + } + + public contains(other: CodeActionKind): boolean { + return this.equals(other) || this.value === '' || startsWith(other.value, this.value + CodeActionKind.sep); + } + + public intersects(other: CodeActionKind): boolean { + return this.contains(other) || other.contains(this); + } + + public append(part: string): CodeActionKind { + return new CodeActionKind(this.value + CodeActionKind.sep + part); + } +} + +export const enum CodeActionAutoApply { + IfSingle = 'ifSingle', + First = 'first', + Never = 'never', +} + +export interface CodeActionFilter { + readonly include?: CodeActionKind; + readonly excludes?: readonly CodeActionKind[]; + readonly includeSourceActions?: boolean; + readonly onlyIncludePreferredActions?: boolean; +} + +export function mayIncludeActionsOfKind(filter: CodeActionFilter, providedKind: CodeActionKind): boolean { + // A provided kind may be a subset or superset of our filtered kind. + if (filter.include && !filter.include.intersects(providedKind)) { + return false; + } + + if (filter.excludes) { + if (filter.excludes.some(exclude => excludesAction(providedKind, exclude, filter.include))) { + return false; + } + } + + // Don't return source actions unless they are explicitly requested + if (!filter.includeSourceActions && CodeActionKind.Source.contains(providedKind)) { + return false; + } + + return true; +} + +export function filtersAction(filter: CodeActionFilter, action: CodeAction): boolean { + const actionKind = action.kind ? new CodeActionKind(action.kind) : undefined; + + // Filter out actions by kind + if (filter.include) { + if (!actionKind || !filter.include.contains(actionKind)) { + return false; + } + } + + if (filter.excludes) { + if (actionKind && filter.excludes.some(exclude => excludesAction(actionKind, exclude, filter.include))) { + return false; + } + } + + // Don't return source actions unless they are explicitly requested + if (!filter.includeSourceActions) { + if (actionKind && CodeActionKind.Source.contains(actionKind)) { + return false; + } + } + + if (filter.onlyIncludePreferredActions) { + if (!action.isPreferred) { + return false; + } + } + + return true; +} + +function excludesAction(providedKind: CodeActionKind, exclude: CodeActionKind, include: CodeActionKind | undefined): boolean { + if (!exclude.contains(providedKind)) { + return false; + } + if (include && exclude.contains(include)) { + // The include is more specific, don't filter out + return false; + } + return true; +} + +export interface CodeActionTrigger { + readonly type: CodeActionTriggerType; + readonly filter?: CodeActionFilter; + readonly autoApply?: CodeActionAutoApply; + readonly context?: { + readonly notAvailableMessage: string; + readonly position: Position; + }; +} + +export class CodeActionCommandArgs { + public static fromUser(arg: any, defaults: { kind: CodeActionKind, apply: CodeActionAutoApply }): CodeActionCommandArgs { + if (!arg || typeof arg !== 'object') { + return new CodeActionCommandArgs(defaults.kind, defaults.apply, false); + } + return new CodeActionCommandArgs( + CodeActionCommandArgs.getKindFromUser(arg, defaults.kind), + CodeActionCommandArgs.getApplyFromUser(arg, defaults.apply), + CodeActionCommandArgs.getPreferredUser(arg)); + } + + private static getApplyFromUser(arg: any, defaultAutoApply: CodeActionAutoApply) { + switch (typeof arg.apply === 'string' ? arg.apply.toLowerCase() : '') { + case 'first': return CodeActionAutoApply.First; + case 'never': return CodeActionAutoApply.Never; + case 'ifsingle': return CodeActionAutoApply.IfSingle; + default: return defaultAutoApply; + } + } + + private static getKindFromUser(arg: any, defaultKind: CodeActionKind) { + return typeof arg.kind === 'string' + ? new CodeActionKind(arg.kind) + : defaultKind; + } + + private static getPreferredUser(arg: any): boolean { + return typeof arg.preferred === 'boolean' + ? arg.preferred + : false; + } + + private constructor( + public readonly kind: CodeActionKind, + public readonly apply: CodeActionAutoApply, + public readonly preferred: boolean, + ) { } +} diff --git a/src/vs/editor/contrib/codelens/codeLensCache.ts b/src/vs/editor/contrib/codelens/codeLensCache.ts index 2bff6506f43a2..5f8c147657db2 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 { @@ -68,11 +68,18 @@ export class CodeLensCache implements ICodeLensCache { } put(model: ITextModel, data: CodeLensModel): void { + // create a copy of the model that is without command-ids + // but with comand-labels + const copyItems = data.lenses.map(item => { + return { + range: item.symbol.range, + command: item.symbol.command && { id: '', title: item.symbol.command?.title }, + }; + }); + const copyModel = new CodeLensModel(); + copyModel.add({ lenses: copyItems, dispose: () => { } }, this._fakeProvider); - const lensModel = new CodeLensModel(); - lensModel.add({ lenses: data.lenses.map(v => v.symbol), dispose() { } }, this._fakeProvider); - - const item = new CacheItem(model.getLineCount(), lensModel); + const item = new CacheItem(model.getLineCount(), copyModel); this._cache.set(model.uri.toString(), item); } @@ -89,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 76c9cd84fd7f1..fbcfb19913bf4 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); } - }); + } catch (err) { + onUnexpectedExternalError(err); + } + }); - return result; + 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 a4acfe1ed171a..3a4a3f5acb843 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -5,77 +5,103 @@ 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 * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICodeEditor, MouseTargetType, IViewZoneChangeAccessor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; +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'; 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 editorCommon.IEditorContribution { +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 _lenses: CodeLensWidget[] = []; - private _currentFindCodeLensSymbolsPromise: CancelablePromise | undefined; + private readonly _styleElement: HTMLStyleElement; + private readonly _styleClassName: string; + 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: editorBrowser.ICodeEditor, + private readonly _editor: ICodeEditor, @ICommandService private readonly _commandService: ICommandService, @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._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._globalToDispose.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); + this._disposables.add(CodeLensProviderRegistry.onDidChange(this._onModelChange, this)); this._onModelChange(); + + this._styleClassName = '_' + hash(this._editor.getId()).toString(16); + this._styleElement = dom.createStyleSheet( + dom.isInShadowDOM(this._editor.getContainerDomNode()) + ? this._editor.getContainerDomNode() + : undefined + ); + this._updateLensStyle(); } dispose(): void { this._localDispose(); - this._globalToDispose.dispose(); + this._disposables.dispose(); this._oldCodeLensModels.dispose(); - dispose(this._currentCodeLensModel); + this._currentCodeLensModel?.dispose(); + } + + private _updateLensStyle(): void { + const options = this._editor.getOptions(); + const fontInfo = options.get(EditorOption.fontInfo); + const lineHeight = options.get(EditorOption.lineHeight); + + + const height = Math.round(lineHeight * 1.1); + const fontSize = Math.round(fontInfo.fontSize * 0.9); + const newStyle = ` + .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.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 { @@ -87,7 +113,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { return; } - if (!this._isEnabled) { + if (!this._editor.getOption(EditorOption.codeLens)) { return; } @@ -118,34 +144,34 @@ export class CodeLensContribution implements editorCommon.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; + 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); + // cache model to reduce flicker + this._codeLensCache.put(model, result); - // render lenses - this._renderCodeLensSymbols(result); - detectVisibleLenses.schedule(); - } + // 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 => { @@ -174,17 +200,17 @@ export class CodeLensContribution implements editorCommon.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()) { @@ -200,17 +226,17 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { this._disposeAllLenses(undefined, undefined); } })); - this._localToDispose.add(this._editor.onDidChangeConfiguration(e => { - if (e.hasChanged(EditorOption.fontInfo)) { - for (const lens of this._lenses) { - lens.updateHeight(); - } - } - })); this._localToDispose.add(this._editor.onMouseUp(e => { - if (e.target.type === editorBrowser.MouseTargetType.CONTENT_WIDGET && e.target.element && e.target.element.tagName === 'A') { + if (e.target.type !== MouseTargetType.CONTENT_WIDGET) { + return; + } + let target = e.target.element; + if (target?.tagName === 'SPAN') { + target = target.parentElement; + } + if (target?.tagName === 'A') { for (const lens of this._lenses) { - let command = lens.getCommand(e.target.element as HTMLLinkElement); + let command = lens.getCommand(target as HTMLLinkElement); if (command) { this._commandService.executeCommand(command.id, ...(command.arguments || [])).catch(err => this._notificationService.error(err)); break; @@ -221,13 +247,15 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { scheduler.schedule(); } - private _disposeAllLenses(decChangeAccessor: IModelDecorationsChangeAccessor | undefined, viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor | undefined): void { - let helper = new CodeLensHelper(); - this._lenses.forEach((lens) => lens.dispose(helper, viewZoneChangeAccessor)); + private _disposeAllLenses(decChangeAccessor: IModelDecorationsChangeAccessor | undefined, viewZoneChangeAccessor: IViewZoneChangeAccessor | undefined): void { + const helper = new CodeLensHelper(); + for (const lens of this._lenses) { + lens.dispose(helper, viewZoneChangeAccessor); + } if (decChangeAccessor) { helper.commit(decChangeAccessor); } - this._lenses = []; + this._lenses.length = 0; } private _renderCodeLensSymbols(symbols: CodeLensModel): void { @@ -276,7 +304,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { groupsIndex++; codeLensIndex++; } else { - this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, 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++; } @@ -290,7 +318,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { // Create extra symbols while (groupsIndex < groups.length) { - this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, helper, viewZoneAccessor, () => this._detectVisibleLenses && this._detectVisibleLenses.schedule())); + this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, () => this._resolveCodeLensesInViewportSoon())); groupsIndex++; } @@ -301,11 +329,17 @@ export class CodeLensContribution implements editorCommon.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) { @@ -326,7 +360,9 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { return; } - this._currentResolveCodeLensSymbolsPromise = createCancelablePromise(token => { + const t1 = Date.now(); + + const resolvePromise = createCancelablePromise(token => { const promises = toResolve.map((request, i) => { @@ -343,7 +379,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { }); return Promise.all(promises).then(() => { - if (!token.isCancellationRequested) { + if (!token.isCancellationRequested && !lenses[i].isDisposed()) { lenses[i].updateCommands(resolvedSymbols); } }); @@ -351,15 +387,90 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { return Promise.all(promises); }); + this._resolveCodeLensesPromise = resolvePromise; - this._currentResolveCodeLensSymbolsPromise.then(() => { + this._resolveCodeLensesPromise.then(() => { + + // update moving average + const newDelay = this._resolveCodeLensesDelays.update(model, Date.now() - t1); + this._resolveCodeLensesScheduler.delay = newDelay; + + 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 - this._currentResolveCodeLensSymbolsPromise = undefined; + if (resolvePromise === this._resolveCodeLensesPromise) { + this._resolveCodeLensesPromise = undefined; + } }, err => { onUnexpectedError(err); // can also be cancellation! - 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 e266cf8b6a826..7bd66d08fd825 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.css +++ b/src/vs/editor/contrib/codelens/codelensWidget.css @@ -23,12 +23,16 @@ } .monaco-editor .codelens-decoration > a:hover { - text-decoration: underline; cursor: pointer; } -.monaco-editor .codelens-decoration.invisible-cl { - opacity: 0; +.monaco-editor .codelens-decoration .codicon { + vertical-align: middle; + color: currentColor !important; +} + +.monaco-editor .codelens-decoration > a:hover .codicon::before { + cursor: pointer; } @keyframes fadein { diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index c048a6f15410c..b9432f980d2cf 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -5,9 +5,7 @@ import 'vs/css!./codelensWidget'; import * as dom from 'vs/base/browser/dom'; -import { coalesce, isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; +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'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -16,9 +14,9 @@ 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 { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { renderCodicons } from 'vs/base/browser/codicons'; -class CodeLensViewZone implements editorBrowser.IViewZone { +class CodeLensViewZone implements IViewZone { readonly heightInLines: number; readonly suppressMouseDown: boolean; @@ -48,7 +46,7 @@ class CodeLensViewZone implements editorBrowser.IViewZone { } } -class CodeLensContentWidget implements editorBrowser.IContentWidget { +class CodeLensContentWidget implements IContentWidget { private static _idPool: number = 0; @@ -58,69 +56,62 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { private readonly _id: string; private readonly _domNode: HTMLElement; - private readonly _editor: editorBrowser.ICodeEditor; + private readonly _editor: IActiveCodeEditor; private readonly _commands = new Map(); - private _widgetPosition?: editorBrowser.IContentWidgetPosition; + private _widgetPosition?: IContentWidgetPosition; + private _isEmpty: boolean = true; constructor( - editor: editorBrowser.ICodeEditor, - symbolRange: Range, - data: CodeLensItem[] + editor: IActiveCodeEditor, + className: string, + line: number, ) { - this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool); this._editor = editor; + this._id = `codelens.widget-${(CodeLensContentWidget._idPool++)}`; - this.setSymbolRange(symbolRange); + this.updatePosition(line); this._domNode = document.createElement('span'); - this._domNode.innerHTML = ' '; - dom.addClass(this._domNode, 'codelens-decoration'); - this.updateHeight(); - this.withCommands(data.map(data => data.symbol), false); + this._domNode.className = `codelens-decoration ${className}`; } - updateHeight(): void { - const options = this._editor.getOptions(); - const fontInfo = options.get(EditorOption.fontInfo); - const lineHeight = options.get(EditorOption.lineHeight); - this._domNode.style.height = `${Math.round(lineHeight * 1.1)}px`; - this._domNode.style.lineHeight = `${lineHeight}px`; - this._domNode.style.fontSize = `${Math.round(fontInfo.fontSize * 0.9)}px`; - this._domNode.style.paddingRight = `${Math.round(fontInfo.fontSize * 0.45)}px`; - this._domNode.innerHTML = ' '; - } - - withCommands(inSymbols: Array, animate: boolean): void { + withCommands(lenses: Array, animate: boolean): void { this._commands.clear(); - const symbols = coalesce(inSymbols); - if (isFalsyOrEmpty(symbols)) { - this._domNode.innerHTML = 'no commands'; - return; - } - - let html: string[] = []; - for (let i = 0; i < symbols.length; i++) { - const command = symbols[i].command; - if (command) { - const title = renderCodicons(command.title); - let part: string; - if (command.id) { - part = `${title}`; - this._commands.set(String(i), command); + let children: HTMLElement[] = []; + let hasSymbol = false; + for (let i = 0; i < lenses.length; i++) { + const lens = lenses[i]; + if (!lens) { + continue; + } + hasSymbol = true; + if (lens.command) { + const title = renderCodicons(lens.command.title); + if (lens.command.id) { + children.push(dom.$('a', { id: String(i) }, ...title)); + this._commands.set(String(i), lens.command); } else { - part = `${title}`; + children.push(dom.$('span', undefined, ...title)); + } + if (i + 1 < lenses.length) { + children.push(dom.$('span', undefined, '\u00a0|\u00a0')); } - html.push(part); } } - const wasEmpty = this._domNode.innerHTML === '' || this._domNode.innerHTML === ' '; - this._domNode.innerHTML = html.join(' | '); - this._editor.layoutContentWidget(this); - if (wasEmpty && animate) { - dom.addClass(this._domNode, 'fadein'); + if (!hasSymbol) { + // symbols but no commands + dom.reset(this._domNode, dom.$('span', undefined, 'no commands')); + + } else { + // symbols and commands + dom.reset(this._domNode, ...children); + if (this._isEmpty && animate) { + this._domNode.classList.add('fadein'); + } + this._isEmpty = false; } } @@ -138,25 +129,17 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { return this._domNode; } - setSymbolRange(range: Range): void { - if (!this._editor.hasModel()) { - return; - } - const lineNumber = range.startLineNumber; - const column = this._editor.getModel().getLineFirstNonWhitespaceColumn(lineNumber); + updatePosition(line: number): void { + const column = this._editor.getModel().getLineFirstNonWhitespaceColumn(line); this._widgetPosition = { - position: { lineNumber: lineNumber, column: column }, - preference: [editorBrowser.ContentWidgetPositionPreference.ABOVE] + position: { lineNumber: line, column: column }, + preference: [ContentWidgetPositionPreference.ABOVE] }; } - getPosition(): editorBrowser.IContentWidgetPosition | null { + getPosition(): IContentWidgetPosition | null { return this._widgetPosition || null; } - - isVisible(): boolean { - return this._domNode.hasAttribute('monaco-visible-content-widget'); - } } export interface IDecorationIdCallback { @@ -194,27 +177,40 @@ export class CodeLensHelper { export class CodeLensWidget { - private readonly _editor: editorBrowser.ICodeEditor; + private readonly _editor: IActiveCodeEditor; + private readonly _className: string; private readonly _viewZone!: CodeLensViewZone; private readonly _viewZoneId!: string; - private readonly _contentWidget!: CodeLensContentWidget; + + private _contentWidget?: CodeLensContentWidget; private _decorationIds: string[]; private _data: CodeLensItem[]; + private _isDisposed: boolean = false; constructor( data: CodeLensItem[], - editor: editorBrowser.ICodeEditor, + editor: IActiveCodeEditor, + className: string, helper: CodeLensHelper, - viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor, + viewZoneChangeAccessor: IViewZoneChangeAccessor, updateCallback: Function ) { this._editor = editor; + this._className = className; this._data = data; - this._decorationIds = new Array(this._data.length); + // create combined range, track all ranges with decorations, + // check if there is already something to render + this._decorationIds = []; let range: Range | undefined; + let lenses: CodeLens[] = []; + this._data.forEach((codeLensData, i) => { + if (codeLensData.symbol.command) { + lenses.push(codeLensData.symbol); + } + helper.addDecoration({ range: codeLensData.symbol.range, options: ModelDecorationOptions.EMPTY @@ -228,43 +224,51 @@ export class CodeLensWidget { } }); - if (range) { - this._contentWidget = new CodeLensContentWidget(editor, range, this._data); - this._viewZone = new CodeLensViewZone(range.startLineNumber - 1, updateCallback); + this._viewZone = new CodeLensViewZone(range!.startLineNumber - 1, updateCallback); + this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); - this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone); - this._editor.addContentWidget(this._contentWidget); + if (lenses.length > 0) { + this._createContentWidgetIfNecessary(); + this._contentWidget!.withCommands(lenses, false); } } - dispose(helper: CodeLensHelper, viewZoneChangeAccessor?: editorBrowser.IViewZoneChangeAccessor): void { - while (this._decorationIds.length) { - helper.removeDecoration(this._decorationIds.pop()!); + private _createContentWidgetIfNecessary(): void { + if (!this._contentWidget) { + this._contentWidget = new CodeLensContentWidget(this._editor, this._className, this._viewZone.afterLineNumber + 1); + this._editor.addContentWidget(this._contentWidget!); } + } + + dispose(helper: CodeLensHelper, viewZoneChangeAccessor?: IViewZoneChangeAccessor): void { + this._decorationIds.forEach(helper.removeDecoration, helper); + this._decorationIds = []; if (viewZoneChangeAccessor) { viewZoneChangeAccessor.removeZone(this._viewZoneId); } - this._editor.removeContentWidget(this._contentWidget); + if (this._contentWidget) { + this._editor.removeContentWidget(this._contentWidget); + this._contentWidget = undefined; + } + this._isDisposed = true; + } + + isDisposed(): boolean { + return this._isDisposed; } isValid(): boolean { - if (!this._editor.hasModel()) { - return false; - } - const model = this._editor.getModel(); return this._decorationIds.some((id, i) => { - const range = model.getDecorationRange(id); + const range = this._editor.getModel().getDecorationRange(id); const symbol = this._data[i].symbol; return !!(range && Range.isEmpty(symbol.range) === range.isEmpty()); }); } updateCodeLensSymbols(data: CodeLensItem[], helper: CodeLensHelper): void { - while (this._decorationIds.length) { - helper.removeDecoration(this._decorationIds.pop()!); - } + this._decorationIds.forEach(helper.removeDecoration, helper); + this._decorationIds = []; this._data = data; - this._decorationIds = new Array(this._data.length); this._data.forEach((codeLensData, i) => { helper.addDecoration({ range: codeLensData.symbol.range, @@ -274,7 +278,7 @@ export class CodeLensWidget { } computeIfNecessary(model: ITextModel): CodeLensItem[] | null { - if (!this._contentWidget.isVisible()) { + if (!this._viewZone.domNode.hasAttribute('monaco-visible-view-zone')) { return null; } @@ -289,7 +293,10 @@ export class CodeLensWidget { } updateCommands(symbols: Array): void { - this._contentWidget.withCommands(symbols, true); + + this._createContentWidgetIfNecessary(); + this._contentWidget!.withCommands(symbols, true); + for (let i = 0; i < this._data.length; i++) { const resolved = symbols[i]; if (resolved) { @@ -299,45 +306,47 @@ export class CodeLensWidget { } } - updateHeight(): void { - this._contentWidget.updateHeight(); - } - getCommand(link: HTMLLinkElement): Command | undefined { - return this._contentWidget.getCommand(link); + return this._contentWidget?.getCommand(link); } getLineNumber(): number { - if (this._editor.hasModel()) { - const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); - if (range) { - return range.startLineNumber; - } + const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); + if (range) { + return range.startLineNumber; } return -1; } - update(viewZoneChangeAccessor: editorBrowser.IViewZoneChangeAccessor): void { - if (this.isValid() && this._editor.hasModel()) { + update(viewZoneChangeAccessor: IViewZoneChangeAccessor): void { + if (this.isValid()) { const range = this._editor.getModel().getDecorationRange(this._decorationIds[0]); if (range) { this._viewZone.afterLineNumber = range.startLineNumber - 1; viewZoneChangeAccessor.layoutZone(this._viewZoneId); - this._contentWidget.setSymbolRange(range); - this._editor.layoutContentWidget(this._contentWidget); + if (this._contentWidget) { + this._contentWidget.updatePosition(range.startLineNumber); + this._editor.layoutContentWidget(this._contentWidget); + } } } } + + getItems(): CodeLensItem[] { + return this._data; + } } registerThemingParticipant((theme, collector) => { const codeLensForeground = theme.getColor(editorCodeLensForeground); if (codeLensForeground) { collector.addRule(`.monaco-editor .codelens-decoration { color: ${codeLensForeground}; }`); + collector.addRule(`.monaco-editor .codelens-decoration .codicon { color: ${codeLensForeground}; }`); } const activeLinkForeground = theme.getColor(editorActiveLinkForeground); if (activeLinkForeground) { collector.addRule(`.monaco-editor .codelens-decoration > a:hover { color: ${activeLinkForeground} !important; }`); + collector.addRule(`.monaco-editor .codelens-decoration > a:hover .codicon { color: ${activeLinkForeground} !important; }`); } }); diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 9e2113c38d90a..4b678d0edda7b 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; @@ -192,7 +192,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { border: 'solid 0.1em #eee' } } - }); + }, undefined, this._editor); } newDecorationsTypes[key] = true; diff --git a/src/vs/editor/contrib/colorPicker/colorPickerModel.ts b/src/vs/editor/contrib/colorPicker/colorPickerModel.ts index 9b9748767321e..772f252613fd2 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 4a74a4a5e0d12..4bcdaa3974d2a 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(); } @@ -150,7 +150,7 @@ class SaturationBox extends Disposable { this.layout(); - this._register(dom.addDisposableListener(this.domNode, dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e))); + this._register(dom.addDisposableGenericMouseDownListner(this.domNode, e => this.onMouseDown(e))); this._register(this.model.onDidChangeColor(this.onDidChangeColor, this)); this.monitor = null; } @@ -163,9 +163,9 @@ class SaturationBox extends Disposable { this.onDidChangePosition(e.offsetX, e.offsetY); } - this.monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null); + this.monitor.startMonitoring(e.target, e.buttons, standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null); - const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => { + const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => { this._onColorFlushed.fire(); mouseUpListener.dispose(); if (this.monitor) { @@ -250,7 +250,7 @@ abstract class Strip extends Disposable { this.slider = dom.append(this.domNode, $('.slider')); this.slider.style.top = `0px`; - this._register(dom.addDisposableListener(this.domNode, dom.EventType.MOUSE_DOWN, e => this.onMouseDown(e))); + this._register(dom.addDisposableGenericMouseDownListner(this.domNode, e => this.onMouseDown(e))); this.layout(); } @@ -264,19 +264,19 @@ 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); } - monitor.startMonitoring(standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null); + monitor.startMonitoring(e.target, e.buttons, standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null); - const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => { + const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => { 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 5c5273c9c3595..0a6d6a59ea585 100644 --- a/src/vs/editor/contrib/comment/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/blockCommentCommand.ts @@ -8,17 +8,19 @@ 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 * as editorCommon from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; +import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -export class BlockCommentCommand implements editorCommon.ICommand { +export class BlockCommentCommand implements ICommand { private readonly _selection: Selection; + private readonly _insertSpace: boolean; private _usedEndToken: string | null; - constructor(selection: Selection) { + constructor(selection: Selection, insertSpace: boolean) { this._selection = selection; + this._insertSpace = insertSpace; this._usedEndToken = null; } @@ -53,7 +55,7 @@ export class BlockCommentCommand implements editorCommon.ICommand { return true; } - private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, insertSpace: boolean, model: ITextModel, builder: IEditOperationBuilder): void { const startLineNumber = selection.startLineNumber; const startColumn = selection.startColumn; const endLineNumber = selection.endLineNumber; @@ -91,25 +93,21 @@ export class BlockCommentCommand implements editorCommon.ICommand { if (startTokenIndex !== -1 && endTokenIndex !== -1) { // Consider spaces as part of the comment tokens - if (startTokenIndex + startToken.length < startLineText.length) { - if (startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) { - // Pretend the start token contains a trailing space - startToken = startToken + ' '; - } + if (insertSpace && startTokenIndex + startToken.length < startLineText.length && startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) { + // Pretend the start token contains a trailing space + startToken = startToken + ' '; } - if (endTokenIndex > 0) { - if (endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) { - // Pretend the end token contains a leading space - endToken = ' ' + endToken; - endTokenIndex -= 1; - } + if (insertSpace && endTokenIndex > 0 && endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) { + // Pretend the end token contains a leading space + endToken = ' ' + endToken; + endTokenIndex -= 1; } ops = BlockCommentCommand._createRemoveBlockCommentOperations( new Range(startLineNumber, startTokenIndex + startToken.length + 1, endLineNumber, endTokenIndex + 1), startToken, endToken ); } else { - ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken); + ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken, this._insertSpace); this._usedEndToken = ops.length === 1 ? endToken : null; } @@ -144,15 +142,15 @@ export class BlockCommentCommand implements editorCommon.ICommand { return res; } - public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string): IIdentifiedSingleEditOperation[] { + public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string, insertSpace: boolean): IIdentifiedSingleEditOperation[] { let res: IIdentifiedSingleEditOperation[] = []; if (!Range.isEmpty(r)) { // Insert block comment start - res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + ' ')); + res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + (insertSpace ? ' ' : ''))); // Insert block comment end - res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), ' ' + endToken)); + res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), (insertSpace ? ' ' : '') + endToken)); } else { // Insert both continuously res.push(EditOperation.replace(new Range( @@ -164,7 +162,7 @@ export class BlockCommentCommand implements editorCommon.ICommand { return res; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { const startLineNumber = this._selection.startLineNumber; const startColumn = this._selection.startColumn; @@ -176,10 +174,10 @@ export class BlockCommentCommand implements editorCommon.ICommand { return; } - this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, model, builder); + this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, this._insertSpace, model, builder); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { const inverseEditOperations = helper.getInverseEditOperations(); if (inverseEditOperations.length === 2) { const startTokenEditOperation = inverseEditOperations[0]; diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index 5c6118a3f6f1f..1dbe337e1ce83 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -12,6 +12,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; import { LineCommentCommand, Type } from 'vs/editor/contrib/comment/lineCommentCommand'; import { MenuId } from 'vs/platform/actions/common/actions'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; abstract class CommentLineAction extends EditorAction { @@ -28,13 +29,20 @@ abstract class CommentLineAction extends EditorAction { return; } - let model = editor.getModel(); - let commands: ICommand[] = []; - let selections = editor.getSelections(); - let opts = model.getOptions(); + const model = editor.getModel(); + const commands: ICommand[] = []; + const selections = editor.getSelections(); + const modelOptions = model.getOptions(); + const commentsOptions = editor.getOption(EditorOption.comments); for (const selection of selections) { - commands.push(new LineCommentCommand(selection, opts.tabSize, this._type)); + commands.push(new LineCommentCommand( + selection, + modelOptions.tabSize, + this._type, + commentsOptions.insertSpace, + commentsOptions.ignoreEmptyLines + )); } editor.pushUndoStop(); @@ -56,7 +64,7 @@ class ToggleCommentLineAction extends CommentLineAction { primary: KeyMod.CtrlCmd | KeyCode.US_SLASH, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '5_insert', title: nls.localize({ key: 'miToggleLineComment', comment: ['&& denotes a mnemonic'] }, "&&Toggle Line Comment"), @@ -112,7 +120,7 @@ class BlockCommentAction extends EditorAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '5_insert', title: nls.localize({ key: 'miToggleBlockComment', comment: ['&& denotes a mnemonic'] }, "Toggle &&Block Comment"), @@ -126,10 +134,11 @@ class BlockCommentAction extends EditorAction { return; } - let commands: ICommand[] = []; - let selections = editor.getSelections(); + const commentsOptions = editor.getOption(EditorOption.comments); + const commands: ICommand[] = []; + const selections = editor.getSelections(); for (const selection of selections) { - commands.push(new BlockCommentCommand(selection)); + commands.push(new BlockCommentCommand(selection, commentsOptions.insertSpace)); } editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index 5c3716ed79cdb..8952727a9d99b 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -9,7 +9,7 @@ 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 * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; @@ -47,22 +47,32 @@ export const enum Type { ForceRemove = 2 } -export class LineCommentCommand implements editorCommon.ICommand { +export class LineCommentCommand implements ICommand { private readonly _selection: Selection; + 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; - private readonly _tabSize: number; - private readonly _type: Type; - constructor(selection: Selection, tabSize: number, type: Type) { + constructor( + selection: Selection, + tabSize: number, + type: Type, + insertSpace: boolean, + ignoreEmptyLines: boolean + ) { this._selection = selection; - this._selectionId = null; this._tabSize = tabSize; this._type = type; + this._insertSpace = insertSpace; + this._selectionId = null; this._deltaColumn = 0; this._moveEndPositionDown = false; + this._ignoreEmptyLines = ignoreEmptyLines; } /** @@ -98,7 +108,7 @@ export class LineCommentCommand implements editorCommon.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, 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; @@ -119,13 +129,7 @@ export class LineCommentCommand implements editorCommon.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; } @@ -145,7 +149,8 @@ export class LineCommentCommand implements editorCommon.ICommand { } } - if (shouldRemoveComments) { + if (shouldRemoveComments && insertSpace) { + // Remove a following space if present const commentStrEndOffset = lineContentStartOffset + lineData.commentStrLength; if (commentStrEndOffset < lineContent.length && lineContent.charCodeAt(commentStrEndOffset) === CharCode.Space) { lineData.commentStrLength += 1; @@ -173,7 +178,7 @@ export class LineCommentCommand implements editorCommon.ICommand { /** * Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments */ - public static _gatherPreflightData(type: Type, 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 { @@ -181,13 +186,13 @@ export class LineCommentCommand implements editorCommon.ICommand { }; } - return LineCommentCommand._analyzeLines(type, model, lines, startLineNumber); + return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber, ignoreEmptyLines); } /** * Given a successful analysis, execute either insert line comments, either remove line comments */ - private _executeLineComments(model: ISimpleModel, builder: editorCommon.IEditOperationBuilder, data: IPreflightDataSupported, s: Selection): void { + private _executeLineComments(model: ISimpleModel, builder: IEditOperationBuilder, data: IPreflightDataSupported, s: Selection): void { let ops: IIdentifiedSingleEditOperation[]; @@ -195,14 +200,14 @@ export class LineCommentCommand implements editorCommon.ICommand { ops = LineCommentCommand._createRemoveLineCommentsOperations(data.lines, s.startLineNumber); } else { LineCommentCommand._normalizeInsertionPoint(model, data.lines, s.startLineNumber, this._tabSize); - ops = LineCommentCommand._createAddLineCommentsOperations(data.lines, s.startLineNumber); + ops = this._createAddLineCommentsOperations(data.lines, s.startLineNumber); } const cursorPosition = new Position(s.positionLineNumber, s.positionColumn); 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; @@ -266,7 +271,7 @@ export class LineCommentCommand implements editorCommon.ICommand { /** * Given an unsuccessful analysis, delegate to the block comment command */ - private _executeBlockComment(model: ITextModel, builder: editorCommon.IEditOperationBuilder, s: Selection): void { + private _executeBlockComment(model: ITextModel, builder: IEditOperationBuilder, s: Selection): void { model.tokenizeIfCheap(s.startLineNumber); let languageId = model.getLanguageIdAtPosition(s.startLineNumber, 1); let config = LanguageConfigurationRegistry.getComments(languageId); @@ -288,11 +293,17 @@ export class LineCommentCommand implements editorCommon.ICommand { firstNonWhitespaceIndex = lineContent.length; } ops = BlockCommentCommand._createAddBlockCommentOperations( - new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1), startToken, endToken + new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1), + startToken, + endToken, + this._insertSpace ); } else { ops = BlockCommentCommand._createAddBlockCommentOperations( - new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), startToken, endToken + new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), + startToken, + endToken, + this._insertSpace ); } @@ -307,7 +318,7 @@ export class LineCommentCommand implements editorCommon.ICommand { } } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let s = this._selection; this._moveEndPositionDown = false; @@ -317,7 +328,15 @@ export class LineCommentCommand implements editorCommon.ICommand { s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); } - const data = LineCommentCommand._gatherPreflightData(this._type, 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); } @@ -325,7 +344,7 @@ export class LineCommentCommand implements editorCommon.ICommand { return this._executeBlockComment(model, builder, s); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { let result = helper.getTrackedSelection(this._selectionId!); if (this._moveEndPositionDown) { @@ -365,8 +384,10 @@ export class LineCommentCommand implements editorCommon.ICommand { /** * Generate edit operations in the add line comment case */ - public static _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] { + private _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] { let res: IIdentifiedSingleEditOperation[] = []; + const afterCommentStr = this._insertSpace ? ' ' : ''; + for (let i = 0, len = lines.length; i < len; i++) { const lineData = lines[i]; @@ -375,13 +396,12 @@ export class LineCommentCommand implements editorCommon.ICommand { continue; } - res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + ' ')); + res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + afterCommentStr)); } return res; } - // TODO@Alex -> duplicated in characterHardWrappingLineMapper private static nextVisibleColumn(currentVisibleColumn: number, tabSize: number, isTab: boolean, columnSize: number): number { if (isTab) { return currentVisibleColumn + (tabSize - (currentVisibleColumn % tabSize)); diff --git a/src/vs/editor/contrib/comment/test/blockCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/blockCommentCommand.test.ts index 8576680d71beb..19a5be49ffb50 100644 --- a/src/vs/editor/contrib/comment/test/blockCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/blockCommentCommand.test.ts @@ -9,7 +9,7 @@ import { CommentMode } from 'vs/editor/test/common/commentMode'; function testBlockCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, true), expectedLines, expectedSelection); mode.dispose(); } @@ -468,4 +468,45 @@ suite('Editor Contrib - Block Comment Command', () => { new Selection(1, 1, 1, 1) ); }); + + test('', () => { + }); + + test('insertSpace false', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, false), expectedLines, expectedSelection); + mode.dispose(); + } + + testLineCommentCommand( + [ + 'some text' + ], + new Selection(1, 1, 1, 5), + [ + '<0some0> text' + ], + new Selection(1, 3, 1, 7) + ); + }); + + test('insertSpace false does not remove space', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, false), expectedLines, expectedSelection); + mode.dispose(); + } + + testLineCommentCommand( + [ + '<0 some 0> text' + ], + new Selection(1, 4, 1, 8), + [ + ' some text' + ], + new Selection(1, 1, 1, 7) + ); + }); }); diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index 5f8b23c6ed74f..0689b4209d583 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -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. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { Selection } from 'vs/editor/common/core/selection'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; @@ -18,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), 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), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -46,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), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -85,12 +86,12 @@ suite('Editor Contrib - Line Comment Command', () => { test('_analyzeLines', () => { let r: IPreflightData; - r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([ + r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([ '\t\t', ' ', ' c', '\t\td' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); if (!r.supported) { throw new Error(`unexpected`); } @@ -116,12 +117,12 @@ suite('Editor Contrib - Line Comment Command', () => { assert.equal(r.lines[3].commentStrOffset, 2); - r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([ + r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([ '\t\t', ' rem ', ' !@# c', '\t\t!@#d' - ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1); + ]), createBasicLinePreflightData(['//', 'rem', '!@#', '!@#']), 1, true); if (!r.supported) { throw new Error(`unexpected`); } @@ -626,13 +627,142 @@ suite('Editor Contrib - Line Comment Command', () => { new Selection(2, 11, 1, 1) ); }); + + 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, true), expectedLines, expectedSelection); + mode.dispose(); + } + + testLineCommentCommand( + [ + 'some text' + ], + new Selection(1, 1, 1, 1), + [ + '!@#some text' + ], + new Selection(1, 4, 1, 4) + ); + }); + + 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, true), expectedLines, expectedSelection); + mode.dispose(); + } + + testLineCommentCommand( + [ + '!@# some text' + ], + new Selection(1, 1, 1, 1), + [ + ' some text' + ], + 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), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -743,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), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true, true), expectedLines, expectedSelection); mode.dispose(); } @@ -984,7 +1114,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { lines, outerMode.getLanguageIdentifier(), selection, - (sel) => new LineCommentCommand(sel, 4, Type.Toggle), + (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 ea1d5f26b1747..8a2a53b8dde01 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -6,16 +6,15 @@ 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'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -23,6 +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 { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; export class ContextMenuController implements IEditorContribution { @@ -49,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) => { @@ -128,7 +135,7 @@ export class ContextMenuController implements IEditorContribution { } // Find actions available for menu - const menuActions = this._getMenuActions(this._editor.getModel()); + const menuActions = this._getMenuActions(this._editor.getModel(), MenuId.EditorContext); // Show menu if we have actions to show if (menuActions.length > 0) { @@ -136,23 +143,44 @@ export class ContextMenuController implements IEditorContribution { } } - private _getMenuActions(model: ITextModel): ReadonlyArray { + private _getMenuActions(model: ITextModel, menuId: MenuId): IAction[] { const result: IAction[] = []; - let contextMenu = this._menuService.createMenu(MenuId.EditorContext, this._contextKeyService); - const groups = contextMenu.getActions({ arg: model.uri }); - contextMenu.dispose(); + // get menu groups + const menu = this._menuService.createMenu(menuId, this._contextKeyService); + const groups = menu.getActions({ arg: model.uri }); + menu.dispose(); + // translate them into other actions for (let group of groups) { const [, actions] = group; - result.push(...actions); - result.push(new Separator()); + let addedItems = 0; + for (const action of actions) { + if (action instanceof SubmenuItemAction) { + const subActions = this._getMenuActions(model, action.item.submenu); + if (subActions.length > 0) { + result.push(new SubmenuAction(action.id, action.label, subActions)); + addedItems++; + } + } else { + result.push(action); + addedItems++; + } + } + + if (addedItems) { + result.push(new Separator()); + } + } + + if (result.length) { + result.pop(); // remove last separator } - result.pop(); // remove last separator + 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; } @@ -183,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/cursorUndo/cursorUndo.ts b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts index aecd70264982a..c23dea3649b70 100644 --- a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts +++ b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts @@ -9,7 +9,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Selection } from 'vs/editor/common/core/selection'; -import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -35,6 +35,14 @@ class CursorState { } } +class StackElement { + constructor( + public readonly cursorState: CursorState, + public readonly scrollTop: number, + public readonly scrollLeft: number + ) { } +} + export class CursorUndoRedoController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.cursorUndoRedoController'; @@ -46,8 +54,8 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr private readonly _editor: ICodeEditor; private _isCursorUndoRedo: boolean; - private _undoStack: CursorState[]; - private _redoStack: CursorState[]; + private _undoStack: StackElement[]; + private _redoStack: StackElement[]; constructor(editor: ICodeEditor) { super(); @@ -76,9 +84,9 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr return; } const prevState = new CursorState(e.oldSelections); - const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].equals(prevState)); + const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].cursorState.equals(prevState)); if (!isEqualToLastUndoStack) { - this._undoStack.push(prevState); + this._undoStack.push(new StackElement(prevState, editor.getScrollTop(), editor.getScrollLeft())); this._redoStack = []; if (this._undoStack.length > 50) { // keep the cursor undo stack bounded @@ -93,7 +101,7 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr return; } - this._redoStack.push(new CursorState(this._editor.getSelections())); + this._redoStack.push(new StackElement(new CursorState(this._editor.getSelections()), this._editor.getScrollTop(), this._editor.getScrollLeft())); this._applyState(this._undoStack.pop()!); } @@ -102,14 +110,17 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr return; } - this._undoStack.push(new CursorState(this._editor.getSelections())); + this._undoStack.push(new StackElement(new CursorState(this._editor.getSelections()), this._editor.getScrollTop(), this._editor.getScrollLeft())); this._applyState(this._redoStack.pop()!); } - private _applyState(state: CursorState): void { + private _applyState(stackElement: StackElement): void { this._isCursorUndoRedo = true; - this._editor.setSelections(state.selections); - this._editor.revealRangeInCenterIfOutsideViewport(state.selections[0], ScrollType.Smooth); + this._editor.setSelections(stackElement.cursorState.selections); + this._editor.setScrollPosition({ + scrollTop: stackElement.scrollTop, + scrollLeft: stackElement.scrollLeft + }); this._isCursorUndoRedo = false; } } diff --git a/src/vs/editor/contrib/dnd/dnd.ts b/src/vs/editor/contrib/dnd/dnd.ts index f9c0cd0090276..b4bdd4336fd51 100644 --- a/src/vs/editor/contrib/dnd/dnd.ts +++ b/src/vs/editor/contrib/dnd/dnd.ts @@ -8,9 +8,9 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IEditorMouseEvent, IMouseTarget, MouseTargetType, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -29,7 +29,7 @@ function hasTriggerModifier(e: IKeyboardEvent | IMouseEvent): boolean { } } -export class DragAndDropController extends Disposable implements editorCommon.IEditorContribution { +export class DragAndDropController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.dragAndDrop'; @@ -50,10 +50,11 @@ export class DragAndDropController extends Disposable implements editorCommon.IE this._register(this._editor.onMouseDown((e: IEditorMouseEvent) => this._onEditorMouseDown(e))); this._register(this._editor.onMouseUp((e: IEditorMouseEvent) => this._onEditorMouseUp(e))); this._register(this._editor.onMouseDrag((e: IEditorMouseEvent) => this._onEditorMouseDrag(e))); - this._register(this._editor.onMouseDrop((e: IEditorMouseEvent) => this._onEditorMouseDrop(e))); + this._register(this._editor.onMouseDrop((e: IPartialEditorMouseEvent) => this._onEditorMouseDrop(e))); 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 editorCommon.IE } 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 editorCommon.IE } private onEditorKeyUp(e: IKeyboardEvent): void { - if (!this._editor.getOption(EditorOption.dragAndDrop)) { + if (!this._editor.getOption(EditorOption.dragAndDrop) || this._editor.getOption(EditorOption.columnSelection)) { return; } @@ -143,7 +144,7 @@ export class DragAndDropController extends Disposable implements editorCommon.IE } } - private _onEditorMouseDrop(mouseEvent: IEditorMouseEvent): void { + private _onEditorMouseDrop(mouseEvent: IPartialEditorMouseEvent): void { if (mouseEvent.target && (this._hitContent(mouseEvent.target) || this._hitMargin(mouseEvent.target)) && mouseEvent.target.position) { let newCursorPosition = new Position(mouseEvent.target.position.lineNumber, mouseEvent.target.position.column); @@ -201,7 +202,7 @@ export class DragAndDropController extends Disposable implements editorCommon.IE }]; this._dndDecorationIds = this._editor.deltaDecorations(this._dndDecorationIds, newDecorations); - this._editor.revealPosition(position, editorCommon.ScrollType.Immediate); + this._editor.revealPosition(position, ScrollType.Immediate); } private _removeDecoration(): void { diff --git a/src/vs/editor/contrib/dnd/dragAndDropCommand.ts b/src/vs/editor/contrib/dnd/dragAndDropCommand.ts index 8d1d8a32b020a..e6d7b427ae59d 100644 --- a/src/vs/editor/contrib/dnd/dragAndDropCommand.ts +++ b/src/vs/editor/contrib/dnd/dragAndDropCommand.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { Selection } from 'vs/editor/common/core/selection'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -export class DragAndDropCommand implements editorCommon.ICommand { +export class DragAndDropCommand implements ICommand { private readonly selection: Selection; private readonly targetPosition: Position; @@ -24,7 +24,7 @@ export class DragAndDropCommand implements editorCommon.ICommand { this.targetSelection = null; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let text = model.getValueInRange(this.selection); if (!this.copy) { builder.addEditOperation(this.selection, null); @@ -102,7 +102,7 @@ export class DragAndDropCommand implements editorCommon.ICommand { } } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { return this.targetSelection!; } } diff --git a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css index e7953bf8ec6b3..fb53f7e85240e 100644 --- a/src/vs/editor/contrib/documentSymbols/media/outlineTree.css +++ b/src/vs/editor/contrib/documentSymbols/media/outlineTree.css @@ -20,10 +20,6 @@ color: var(--outline-element-color); } -.monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail { - visibility: inherit; -} - .monaco-list .outline-element .outline-element-decoration { opacity: 0.75; font-size: 90%; diff --git a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css index 0885772974fd4..a7ba268d15811 100644 --- a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css +++ b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css @@ -3,22 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .monaco-icon-label.deprecated { +.monaco-icon-label.deprecated { text-decoration: line-through; opacity: 0.66; } - -.monaco-workbench .symbol-icon.inline { - display: flex; - align-items: center; - padding-left: 0; -} - -.monaco-workbench .symbol-icon.block { - display: inline-block; - height: 14px; - width: 16px; - min-height: 14px; - min-width: 16px; - background-position: center; -} diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 8059fdbdcfd40..c85081502dd39 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 970453bc12b53..c4bd927fe8392 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, forEach } 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,9 +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; @@ -29,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; } { @@ -82,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) ); } @@ -109,7 +129,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); @@ -278,27 +298,26 @@ export class OutlineFilter implements ITreeFilter { [SymbolKind.TypeParameter]: 'showTypeParameters', }); - private readonly _filteredTypes = new Set(); - constructor( private readonly _prefix: string, - @IConfigurationService private readonly _configService: IConfigurationService, - ) { - this.update(); - } - - update() { - this._filteredTypes.clear(); - forEach(OutlineFilter.configNameToKind, entry => { - const key = `${this._prefix}.${entry.key}`; - if (this._configService.getValue(key) === false) { - this._filteredTypes.add(entry.value); - } - }); - } + @ITextResourceConfigurationService private readonly _textResourceConfigService: ITextResourceConfigurationService, + ) { } filter(element: OutlineItem): boolean { - return !(element instanceof OutlineElement) || !this._filteredTypes.has(element.symbol.kind); + const outline = OutlineModel.get(element); + let uri: URI | undefined; + + if (outline) { + uri = outline.uri; + } + + if (!(element instanceof OutlineElement)) { + return true; + } + + const configName = OutlineFilter.kindToConfigName[element.symbol.kind]; + const configKey = `${this._prefix}.${configName}`; + return this._textResourceConfigService.getValue(uri, configKey); } } @@ -312,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; @@ -329,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(); } } @@ -535,304 +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(` - .monaco-workbench .codicon-symbol-array { - color: ${symbolIconArrayColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolArray.cssSelector} { color: ${symbolIconArrayColor}; }`); } const symbolIconBooleanColor = theme.getColor(SYMBOL_ICON_BOOLEAN_FOREGROUND); if (symbolIconBooleanColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-boolean { - color: ${symbolIconBooleanColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolBoolean.cssSelector} { color: ${symbolIconBooleanColor}; }`); } const symbolIconClassColor = theme.getColor(SYMBOL_ICON_CLASS_FOREGROUND); if (symbolIconClassColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-class { - color: ${symbolIconClassColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolClass.cssSelector} { color: ${symbolIconClassColor}; }`); } const symbolIconMethodColor = theme.getColor(SYMBOL_ICON_METHOD_FOREGROUND); if (symbolIconMethodColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-method { - color: ${symbolIconMethodColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolMethod.cssSelector} { color: ${symbolIconMethodColor}; }`); } const symbolIconColorColor = theme.getColor(SYMBOL_ICON_COLOR_FOREGROUND); if (symbolIconColorColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-color { - color: ${symbolIconColorColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolColor.cssSelector} { color: ${symbolIconColorColor}; }`); } const symbolIconConstantColor = theme.getColor(SYMBOL_ICON_CONSTANT_FOREGROUND); if (symbolIconConstantColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-constant { - color: ${symbolIconConstantColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolConstant.cssSelector} { color: ${symbolIconConstantColor}; }`); } const symbolIconConstructorColor = theme.getColor(SYMBOL_ICON_CONSTRUCTOR_FOREGROUND); if (symbolIconConstructorColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-constructor { - color: ${symbolIconConstructorColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolConstructor.cssSelector} { color: ${symbolIconConstructorColor}; }`); } const symbolIconEnumeratorColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_FOREGROUND); if (symbolIconEnumeratorColor) { collector.addRule(` - .monaco-workbench .codicon-symbol-value, - .monaco-workbench .codicon-symbol-enum { - color: ${symbolIconEnumeratorColor} !important; - } - `); + .monaco-workbench ${Codicon.symbolValue.cssSelector},.monaco-workbench ${Codicon.symbolEnum.cssSelector} { color: ${symbolIconEnumeratorColor}; }`); } const symbolIconEnumeratorMemberColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND); if (symbolIconEnumeratorMemberColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-enum-member { - color: ${symbolIconEnumeratorMemberColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolEnumMember.cssSelector} { color: ${symbolIconEnumeratorMemberColor}; }`); } const symbolIconEventColor = theme.getColor(SYMBOL_ICON_EVENT_FOREGROUND); if (symbolIconEventColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-event { - color: ${symbolIconEventColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolEvent.cssSelector} { color: ${symbolIconEventColor}; }`); } const symbolIconFieldColor = theme.getColor(SYMBOL_ICON_FIELD_FOREGROUND); if (symbolIconFieldColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-field { - color: ${symbolIconFieldColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolField.cssSelector} { color: ${symbolIconFieldColor}; }`); } const symbolIconFileColor = theme.getColor(SYMBOL_ICON_FILE_FOREGROUND); if (symbolIconFileColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-file { - color: ${symbolIconFileColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolFile.cssSelector} { color: ${symbolIconFileColor}; }`); } const symbolIconFolderColor = theme.getColor(SYMBOL_ICON_FOLDER_FOREGROUND); if (symbolIconFolderColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-folder { - color: ${symbolIconFolderColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolFolder.cssSelector} { color: ${symbolIconFolderColor}; }`); } const symbolIconFunctionColor = theme.getColor(SYMBOL_ICON_FUNCTION_FOREGROUND); if (symbolIconFunctionColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-function { - color: ${symbolIconFunctionColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolFunction.cssSelector} { color: ${symbolIconFunctionColor}; }`); } const symbolIconInterfaceColor = theme.getColor(SYMBOL_ICON_INTERFACE_FOREGROUND); if (symbolIconInterfaceColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-interface { - color: ${symbolIconInterfaceColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolInterface.cssSelector} { color: ${symbolIconInterfaceColor}; }`); } const symbolIconKeyColor = theme.getColor(SYMBOL_ICON_KEY_FOREGROUND); if (symbolIconKeyColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-key { - color: ${symbolIconKeyColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolKey.cssSelector} { color: ${symbolIconKeyColor}; }`); } const symbolIconKeywordColor = theme.getColor(SYMBOL_ICON_KEYWORD_FOREGROUND); if (symbolIconKeywordColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-keyword { - color: ${symbolIconKeywordColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolKeyword.cssSelector} { color: ${symbolIconKeywordColor}; }`); } const symbolIconModuleColor = theme.getColor(SYMBOL_ICON_MODULE_FOREGROUND); if (symbolIconModuleColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-module { - color: ${symbolIconModuleColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolModule.cssSelector} { color: ${symbolIconModuleColor}; }`); } const outlineNamespaceColor = theme.getColor(SYMBOL_ICON_NAMESPACE_FOREGROUND); if (outlineNamespaceColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-namespace { - color: ${outlineNamespaceColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolNamespace.cssSelector} { color: ${outlineNamespaceColor}; }`); } const symbolIconNullColor = theme.getColor(SYMBOL_ICON_NULL_FOREGROUND); if (symbolIconNullColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-null { - color: ${symbolIconNullColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolNull.cssSelector} { color: ${symbolIconNullColor}; }`); } const symbolIconNumberColor = theme.getColor(SYMBOL_ICON_NUMBER_FOREGROUND); if (symbolIconNumberColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-number { - color: ${symbolIconNumberColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolNumber.cssSelector} { color: ${symbolIconNumberColor}; }`); } const symbolIconObjectColor = theme.getColor(SYMBOL_ICON_OBJECT_FOREGROUND); if (symbolIconObjectColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-object { - color: ${symbolIconObjectColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolObject.cssSelector} { color: ${symbolIconObjectColor}; }`); } const symbolIconOperatorColor = theme.getColor(SYMBOL_ICON_OPERATOR_FOREGROUND); if (symbolIconOperatorColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-operator { - color: ${symbolIconOperatorColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolOperator.cssSelector} { color: ${symbolIconOperatorColor}; }`); } const symbolIconPackageColor = theme.getColor(SYMBOL_ICON_PACKAGE_FOREGROUND); if (symbolIconPackageColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-package { - color: ${symbolIconPackageColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolPackage.cssSelector} { color: ${symbolIconPackageColor}; }`); } const symbolIconPropertyColor = theme.getColor(SYMBOL_ICON_PROPERTY_FOREGROUND); if (symbolIconPropertyColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-property { - color: ${symbolIconPropertyColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolProperty.cssSelector} { color: ${symbolIconPropertyColor}; }`); } const symbolIconReferenceColor = theme.getColor(SYMBOL_ICON_REFERENCE_FOREGROUND); if (symbolIconReferenceColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-reference { - color: ${symbolIconReferenceColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolReference.cssSelector} { color: ${symbolIconReferenceColor}; }`); } const symbolIconSnippetColor = theme.getColor(SYMBOL_ICON_SNIPPET_FOREGROUND); if (symbolIconSnippetColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-snippet { - color: ${symbolIconSnippetColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolSnippet.cssSelector} { color: ${symbolIconSnippetColor}; }`); } const symbolIconStringColor = theme.getColor(SYMBOL_ICON_STRING_FOREGROUND); if (symbolIconStringColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-string { - color: ${symbolIconStringColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolString.cssSelector} { color: ${symbolIconStringColor}; }`); } const symbolIconStructColor = theme.getColor(SYMBOL_ICON_STRUCT_FOREGROUND); if (symbolIconStructColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-struct { - color: ${symbolIconStructColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolStruct.cssSelector} { color: ${symbolIconStructColor}; }`); } const symbolIconTextColor = theme.getColor(SYMBOL_ICON_TEXT_FOREGROUND); if (symbolIconTextColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-text { - color: ${symbolIconTextColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolText.cssSelector} { color: ${symbolIconTextColor}; }`); } const symbolIconTypeParameterColor = theme.getColor(SYMBOL_ICON_TYPEPARAMETER_FOREGROUND); if (symbolIconTypeParameterColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-type-parameter { - color: ${symbolIconTypeParameterColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolTypeParameter.cssSelector} { color: ${symbolIconTypeParameterColor}; }`); } const symbolIconUnitColor = theme.getColor(SYMBOL_ICON_UNIT_FOREGROUND); if (symbolIconUnitColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-unit { - color: ${symbolIconUnitColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${Codicon.symbolUnit.cssSelector} { color: ${symbolIconUnitColor}; }`); } const symbolIconVariableColor = theme.getColor(SYMBOL_ICON_VARIABLE_FOREGROUND); if (symbolIconVariableColor) { - collector.addRule(` - .monaco-workbench .codicon-symbol-variable { - color: ${symbolIconVariableColor} !important; - } - `); + collector.addRule(`.monaco-workbench ${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 b41a7f8dd8f27..37cd2ad17f4f5 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 ae3626007578f..7e452f6a6cdb3 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -9,8 +9,8 @@ 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 * as editorCommon from 'vs/editor/common/editorCommon'; +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 { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget'; @@ -20,13 +20,13 @@ 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'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; const SEARCH_STRING_MAX_LENGTH = 524288; @@ -39,7 +39,7 @@ export function getSelectionSearchString(editor: ICodeEditor): string | null { // if selection spans multiple lines, default search string to empty if (selection.startLineNumber === selection.endLineNumber) { if (selection.isEmpty()) { - let wordAtPosition = editor.getModel().getWordAtPosition(selection.getStartPosition()); + const wordAtPosition = editor.getConfiguredWordAtPosition(selection.getStartPosition()); if (wordAtPosition) { return wordAtPosition.word; } @@ -66,9 +66,10 @@ export interface IFindStartOptions { shouldFocus: FindStartFocusAction; shouldAnimate: boolean; updateSearchScope: boolean; + loop: boolean; } -export class CommonFindController extends Disposable implements editorCommon.IEditorContribution { +export class CommonFindController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.findController'; @@ -125,7 +126,8 @@ export class CommonFindController extends Disposable implements editorCommon.IEd seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, - updateSearchScope: false + updateSearchScope: false, + loop: this._editor.getOption(EditorOption.find).loop }); } })); @@ -230,12 +232,22 @@ export class CommonFindController extends Disposable implements editorCommon.IEd 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 +264,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd // overwritten in subclass } - protected _start(opts: IFindStartOptions): void { + protected async _start(opts: IFindStartOptions): Promise { this.disposeModel(); if (!this._editor.hasModel()) { @@ -276,7 +288,13 @@ export class CommonFindController extends Disposable implements editorCommon.IEd } 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 +308,14 @@ export class CommonFindController extends Disposable implements editorCommon.IEd } 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 +323,8 @@ export class CommonFindController extends Disposable implements editorCommon.IEd } } - public start(opts: IFindStartOptions): void { - this._start(opts); + public start(opts: IFindStartOptions): Promise { + return this._start(opts); } public moveToNextMatch(): boolean { @@ -348,9 +368,8 @@ export class CommonFindController extends Disposable implements editorCommon.IEd 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 +378,12 @@ export class CommonFindController extends Disposable implements editorCommon.IEd 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 +402,15 @@ export class FindController extends CommonFindController implements IFindControl @IThemeService private readonly _themeService: IThemeService, @INotificationService private readonly _notificationService: INotificationService, @IStorageService _storageService: IStorageService, - @optional(IClipboardService) clipboardService: IClipboardService, + @IStorageKeysSyncRegistryService private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, + @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 +436,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(); + } } } @@ -437,12 +459,12 @@ export class FindController extends CommonFindController implements IFindControl } private _createFindWidget() { - this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService)); + this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService, this._storageKeysSyncRegistryService)); this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService)); } } -export class StartFindAction extends EditorAction { +export class StartFindAction extends MultiEditorAction { constructor() { super({ @@ -455,7 +477,7 @@ export class StartFindAction extends EditorAction { primary: KeyMod.CtrlCmd | KeyCode.KEY_F, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '3_find', title: nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"), @@ -464,16 +486,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, seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).globalFindClipboard, shouldFocus: FindStartFocusAction.FocusFindInput, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); } } @@ -498,16 +521,17 @@ export class StartFindWithSelectionAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { let controller = CommonFindController.get(editor); if (controller) { - controller.start({ + await controller.start({ forceRevealReplace: false, seedSearchStringFromSelection: true, seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); controller.setGlobalBufferTerm(controller.getState().searchString); @@ -515,16 +539,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, seedSearchStringFromGlobalClipboard: true, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); this._run(controller); } @@ -620,7 +645,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 +655,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, seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); this._run(controller); } @@ -687,7 +713,7 @@ export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction { } } -export class StartFindReplaceAction extends EditorAction { +export class StartFindReplaceAction extends MultiEditorAction { constructor() { super({ @@ -701,7 +727,7 @@ export class StartFindReplaceAction extends EditorAction { mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarEditMenu, group: '3_find', title: nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"), @@ -710,7 +736,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 +761,14 @@ export class StartFindReplaceAction extends EditorAction { if (controller) { - controller.start({ + await controller.start({ forceRevealReplace: true, seedSearchStringFromSelection: seedSearchStringFromSelection, seedSearchStringFromGlobalClipboard: editor.getOption(EditorOption.find).seedSearchStringFromSelection, shouldFocus: shouldFocus, shouldAnimate: true, - updateSearchScope: false + updateSearchScope: false, + loop: editor.getOption(EditorOption.find).loop }); } } @@ -749,7 +776,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 +785,8 @@ registerEditorAction(PreviousMatchFindAction); registerEditorAction(PreviousMatchFindAction2); registerEditorAction(NextSelectionMatchFindAction); registerEditorAction(PreviousSelectionMatchFindAction); -registerEditorAction(StartFindReplaceAction); +export const EditorStartFindReplaceAction = new StartFindReplaceAction(); +registerMultiEditorAction(EditorStartFindReplaceAction); const FindCommand = EditorCommand.bindToContribution(CommonFindController.get); diff --git a/src/vs/editor/contrib/find/findDecorations.ts b/src/vs/editor/contrib/find/findDecorations.ts index ee93bccc3cf96..c6b966e77c198 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 da67f86eb78ba..a902f36c6d067 100644 --- a/src/vs/editor/contrib/find/findModel.ts +++ b/src/vs/editor/contrib/find/findModel.ts @@ -13,19 +13,20 @@ 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 { Constants } from 'vs/base/common/uint'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ScrollType, ICommand } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference, FindMatch, ITextModel } from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; 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); @@ -168,34 +169,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()); } } @@ -209,7 +229,7 @@ export class FindModelBoundToEditorModel { let findScope = this._decorations.getFindScope(); if (findScope) { // Reveal the selection so user is reminded that 'selection find' is on. - this._editor.revealRangeInCenterIfOutsideViewport(findScope, editorCommon.ScrollType.Smooth); + this._editor.revealRangeInCenterIfOutsideViewport(findScope, ScrollType.Smooth); } return true; } @@ -225,7 +245,7 @@ export class FindModelBoundToEditorModel { ); this._editor.setSelection(match); - this._editor.revealRangeInCenterIfOutsideViewport(match, editorCommon.ScrollType.Smooth); + this._editor.revealRangeInCenterIfOutsideViewport(match, ScrollType.Smooth); } private _prevSearchPosition(before: Position) { @@ -251,6 +271,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 +366,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 +477,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 +490,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 +541,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 +560,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 @@ -536,7 +579,7 @@ export class FindModelBoundToEditorModel { this._editor.setSelections(selections); } - private _executeEditorCommand(source: string, command: editorCommon.ICommand): void { + private _executeEditorCommand(source: string, command: ICommand): void { try { this._ignoreModelContentChanged = true; this._editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/find/findOptionsWidget.ts b/src/vs/editor/contrib/find/findOptionsWidget.ts index b3d9b69e1f53f..d541e0d06086e 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 22451cf9bedf4..0313dd8bc4630 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 5cabcc69c61d1..15ed9eb3f75e6 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -3,42 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* Checkbox */ - -.monaco-checkbox .codicon-selection { - width: 12px; - height: 12px; - border: 1px solid black; - background-color: transparent; - display: inline-block; -} - -.monaco-checkbox .checkbox { - position: absolute; - overflow: hidden; - clip: rect(0 0 0 0); - height: 1px; - width: 1px; - margin: -1px; - padding: 0; - border: 0; -} - -.monaco-checkbox .checkbox:checked + .codicon-selection { - background-color: black; -} - /* 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 { @@ -65,7 +40,6 @@ .monaco-editor .find-widget .monaco-inputbox .input { background-color: transparent; - /* Style to compensate for //winjs */ min-height: 0; } @@ -152,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; @@ -185,40 +155,6 @@ cursor: default; } -.monaco-editor .find-widget .monaco-checkbox { - width: 20px; - height: 20px; - display: inline-block; - vertical-align: middle; - margin-left: 3px; -} - -.monaco-editor .find-widget .monaco-checkbox .codicon-selection { - display: flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - border: none; -} - -.monaco-editor .find-widget .monaco-checkbox .checkbox:disabled + .codicon-selection { - opacity: 0.3; - cursor: default; -} - -.monaco-editor .find-widget .monaco-checkbox .checkbox:not(:disabled) + .codicon-selection { - cursor: pointer; -} - -.monaco-editor .find-widget .monaco-checkbox .checkbox:not(:disabled):hover:before + .codicon-selection { - background-color: #DDD; -} - -.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .codicon-selection { - background-color: rgba(100, 100, 100, 0.2); -} - .monaco-editor .find-widget > .replace-part { display: none; } @@ -239,8 +175,7 @@ } /* REDUCED */ -.monaco-editor .find-widget.reduced-find-widget .matchesCount, -.monaco-editor .find-widget.reduced-find-widget .monaco-checkbox { +.monaco-editor .find-widget.reduced-find-widget .matchesCount { display:none; } @@ -268,18 +203,7 @@ } .monaco-editor .find-widget .monaco-sash { - width: 2px !important; - margin-left: -4px; -} - -.monaco-editor.vs-dark .find-widget .monaco-checkbox .checkbox:not(:disabled):hover:before + .codicon-selection { - background-color: rgba(255, 255, 255, 0.1); -} - -.monaco-editor.hc-black .find-widget .button:not(.disabled):hover, -.monaco-editor.vs-dark .find-widget .button:not(.disabled):hover, -.monaco-editor.vs-dark .find-widget .monaco-checkbox:not(.disabled):hover { - background-color: rgba(255, 255, 255, 0.1); + left: 0 !important; } .monaco-editor.hc-black .find-widget .button:before { @@ -287,7 +211,3 @@ top: 1px; left: 2px; } - -.monaco-editor.hc-black .find-widget .monaco-checkbox .checkbox:checked + .codicon-selection { - background-color: rgba(255, 255, 255, 0.1); -} diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index 5c9aee4a2ed60..9ba13e5aff511 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,29 @@ 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 { INotificationService } from 'vs/platform/notification/common/notification'; +import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +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,14 +68,14 @@ 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; const FIND_INPUT_AREA_WIDTH = PART_WIDTH - 54; let MAX_MATCHES_COUNT_WIDTH = 69; -let FIND_ALL_CONTROLS_WIDTH = 17/** Find Input margin-left */ + (MAX_MATCHES_COUNT_WIDTH + 3 + 1) /** Match Results */ + 23 /** Button */ * 4 + 2/** sash */; +// let FIND_ALL_CONTROLS_WIDTH = 17/** Find Input margin-left */ + (MAX_MATCHES_COUNT_WIDTH + 3 + 1) /** Match Results */ + 23 /** Button */ * 4 + 2/** sash */; const FIND_INPUT_AREA_HEIGHT = 33; // The height of Find Widget when Replace Input is not visible. const ctrlEnterReplaceAllWarningPromptedKey = 'ctrlEnterReplaceAll.windows.donotask'; @@ -101,7 +113,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; @@ -152,6 +164,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas themeService: IThemeService, storageService: IStorageService, notificationService: INotificationService, + storageKeysSyncRegistryService: IStorageKeysSyncRegistryService ) { super(); this._codeEditor = codeEditor; @@ -163,6 +176,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas this._storageService = storageService; this._notificationService = notificationService; + storageKeysSyncRegistryService.registerStorageKey({ key: ctrlEnterReplaceAllWarningPromptedKey, version: 1 }); this._ctrlEnterReplaceAllWarningPrompted = !!storageService.getBoolean(ctrlEnterReplaceAllWarningPromptedKey, StorageScope.GLOBAL); this._isVisible = false; @@ -210,9 +224,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 +258,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) { @@ -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._toggleReplaceBtn.setExpanded(this._isReplaceVisible); let canReplace = !this._codeEditor.getOption(EditorOption.readOnly); @@ -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), @@ -670,7 +702,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas } const editorWidth = layoutInfo.width; - const minimapWidth = layoutInfo.minimapWidth; + const minimapWidth = layoutInfo.minimap.minimapWidth; let collapsedFindWidget = false; let reducedFindWidget = false; let narrowFindWidget = false; @@ -706,10 +738,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (this._resized) { this._findInput.inputBox.layout(); - let findInputWidth = this._findInput.inputBox.width; + let findInputWidth = this._findInput.inputBox.element.clientWidth; if (findInputWidth > 0) { this._replaceInput.width = findInputWidth; } + } else if (this._isReplaceVisible) { + this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode); } } @@ -770,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); } } } @@ -870,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 { @@ -906,8 +943,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return null; } try { - /* tslint:disable-next-line:no-unused-expression */ - 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 }; @@ -966,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); } @@ -975,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); } @@ -993,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 })); @@ -1001,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 { @@ -1019,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); }, @@ -1082,7 +1126,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(); }, @@ -1097,7 +1141,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(); } @@ -1127,8 +1171,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 @@ -1142,7 +1184,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; @@ -1159,13 +1201,11 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas return; } - const inputBoxWidth = width - FIND_ALL_CONTROLS_WIDTH; const maxWidth = parseFloat(dom.getComputedStyle(this._domNode).maxWidth!) || 0; if (width > maxWidth) { return; } this._domNode.style.width = `${width}px`; - this._findInput.inputBox.width = inputBoxWidth; if (this._isReplaceVisible) { this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode); } @@ -1189,7 +1229,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 { /** @@ -1197,10 +1237,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas */ } - const inputBoxWidth = width - FIND_ALL_CONTROLS_WIDTH; this._domNode.style.width = `${width}px`; - this._findInput.inputBox.width = inputBoxWidth; if (this._isReplaceVisible) { this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode); } @@ -1275,10 +1313,13 @@ export class SimpleButton extends Widget { 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) { + dom.removeClasses(this._domNode, findCollapsedIcon.classNames); + dom.addClasses(this._domNode, findExpandedIcon.classNames); + } else { + dom.removeClasses(this._domNode, findExpandedIcon.classNames); + dom.addClasses(this._domNode, findCollapsedIcon.classNames); + } } } @@ -1335,28 +1376,18 @@ 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}; }`); } } - const inputActiveBorder = theme.getColor(inputActiveOptionBorder); - if (inputActiveBorder) { - collector.addRule(`.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { border: 1px solid ${inputActiveBorder.toString()}; }`); - } - - const inputActiveBackground = theme.getColor(inputActiveOptionBackground); - if (inputActiveBackground) { - collector.addRule(`.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { background-color: ${inputActiveBackground.toString()}; }`); - } - // This rule is used to override the outline color for synthetic-focus find input. const focusOutline = theme.getColor(focusBorder); if (focusOutline) { - collector.addRule(`.monaco-workbench .monaco-editor .find-widget .monaco-inputbox.synthetic-focus { outline-color: ${focusOutline}; }`); + collector.addRule(`.monaco-editor .find-widget .monaco-inputbox.synthetic-focus { outline-color: ${focusOutline}; }`); } }); 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 dbe70d742de2d..0000000000000 --- 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 ec824f41cc093..0000000000000 --- 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 5db4f79da8561..0000000000000 --- 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 aac3a5020cd54..0000000000000 --- 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 75644595d19d1..0000000000000 --- 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 cf5f28ca35c31..0000000000000 --- 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 6fc07d81a555c..0000000000000 --- 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 3608b15d29ebb..0000000000000 --- 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 07bd41a789f30..0000000000000 --- 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 cd3974fae7ec8..0000000000000 --- 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 5882b22c58926..0000000000000 --- 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 220f2aba40c07..0000000000000 --- a/src/vs/editor/contrib/find/images/replace-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/editor/contrib/find/replaceAllCommand.ts b/src/vs/editor/contrib/find/replaceAllCommand.ts index bdf2942f014af..fb2de090a52a7 100644 --- a/src/vs/editor/contrib/find/replaceAllCommand.ts +++ b/src/vs/editor/contrib/find/replaceAllCommand.ts @@ -5,7 +5,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; interface IEditOperation { @@ -13,7 +13,7 @@ interface IEditOperation { text: string; } -export class ReplaceAllCommand implements editorCommon.ICommand { +export class ReplaceAllCommand implements ICommand { private readonly _editorSelection: Selection; private _trackedEditorSelectionId: string | null; @@ -27,7 +27,7 @@ export class ReplaceAllCommand implements editorCommon.ICommand { this._trackedEditorSelectionId = null; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { if (this._ranges.length > 0) { // Collect all edit operations let ops: IEditOperation[] = []; @@ -66,7 +66,7 @@ export class ReplaceAllCommand implements editorCommon.ICommand { this._trackedEditorSelectionId = builder.trackSelection(this._editorSelection); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { return helper.getTrackedSelection(this._trackedEditorSelectionId!); } } diff --git a/src/vs/editor/contrib/find/replacePattern.ts b/src/vs/editor/contrib/find/replacePattern.ts index 12a05d93536fe..e829fe6c108cd 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 1cfc4f135f09f..98f31f328cb91 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)); @@ -75,7 +75,7 @@ suite('Find', () => { let searchStringSelectionTwoLines = getSelectionSearchString(editor); assert.equal(searchStringSelectionTwoLines, null); - // Select end of first line newline and and chunk of second + // Select end of first line newline and chunk of second editor.setSelection(new Range(1, 7, 2, 4)); let searchStringSelectionSpanLines = getSelectionSearchString(editor); assert.equal(searchStringSelectionSpanLines, null); diff --git a/src/vs/editor/contrib/find/test/findController.test.ts b/src/vs/editor/contrib/find/test/findController.test.ts index 3065cb082f95e..ee0c4862632be 100644 --- a/src/vs/editor/contrib/find/test/findController.test.ts +++ b/src/vs/editor/contrib/find/test/findController.test.ts @@ -14,7 +14,7 @@ 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 { 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, 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, 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] @@ -428,7 +430,7 @@ suite('FindController', () => { }); }); -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 +447,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 +474,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 +499,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 +516,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 = { forceRevealReplace: false, seedSearchStringFromSelection: false, seedSearchStringFromGlobalClipboard: false, shouldFocus: FindStartFocusAction.NoFocusChange, shouldAnimate: false, - updateSearchScope: true - }); + updateSearchScope: true, + loop: true + }; + + editor.setSelection(new Range(1, 1, 2, 1)); + findController.start(findConfig); + assert.deepEqual(findController.getState().searchScope, [new Selection(1, 1, 2, 1)]); + + findController.closeFindWidget(); - assert.deepEqual(findController.getState().searchScope, new Selection(1, 1, 2, 1)); + 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, 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, 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, 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 df936e17485d2..70c5494985a3e 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 5a110c8160a82..907292fd7ade1 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 d19d4b7353155..a68daff8155c9 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 d000560f648ab..2372e88992262 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -14,8 +14,8 @@ import { ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon'; 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 } from 'vs/editor/contrib/folding/foldingModel'; -import { FoldingDecorationProvider } from './foldingDecorations'; +import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, toggleCollapseState, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel'; +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'; @@ -32,6 +32,8 @@ import { InitializingRangeProvider, ID_INIT_PROVIDER } from 'vs/editor/contrib/f import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; 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, iconForeground } from 'vs/platform/theme/common/colorRegistry'; const CONTEXT_FOLDING_ENABLED = new RawContextKey('foldingEnabled', false); @@ -59,8 +61,8 @@ export class FoldingController extends Disposable implements IEditorContribution private readonly editor: ICodeEditor; private _isEnabled: boolean; - private _autoHideFoldingControls: boolean; private _useFoldingProviders: boolean; + private _unfoldOnClickAfterEndOfLine: boolean; private readonly foldingDecorationProvider: FoldingDecorationProvider; @@ -89,8 +91,8 @@ export class FoldingController extends Disposable implements IEditorContribution this.editor = editor; const options = this.editor.getOptions(); this._isEnabled = options.get(EditorOption.folding); - this._autoHideFoldingControls = options.get(EditorOption.showFoldingControls) === 'mouseover'; this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation'; + this._unfoldOnClickAfterEndOfLine = options.get(EditorOption.unfoldOnClickAfterEndOfLine); this.foldingModel = null; this.hiddenRangeModel = null; @@ -103,32 +105,31 @@ export class FoldingController extends Disposable implements IEditorContribution this.mouseDownInfo = null; this.foldingDecorationProvider = new FoldingDecorationProvider(editor); - this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls; + this.foldingDecorationProvider.autoHideFoldingControls = options.get(EditorOption.showFoldingControls) === 'mouseover'; + this.foldingDecorationProvider.showFoldingHighlights = options.get(EditorOption.foldingHighlight); this.foldingEnabled = CONTEXT_FOLDING_ENABLED.bindTo(this.contextKeyService); this.foldingEnabled.set(this._isEnabled); this._register(this.editor.onDidChangeModel(() => this.onModelChanged())); this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => { - if (e.hasChanged(EditorOption.folding) || e.hasChanged(EditorOption.showFoldingControls) || e.hasChanged(EditorOption.foldingStrategy)) { - let oldIsEnabled = this._isEnabled; - const options = this.editor.getOptions(); - this._isEnabled = options.get(EditorOption.folding); + if (e.hasChanged(EditorOption.folding)) { + this._isEnabled = this.editor.getOptions().get(EditorOption.folding); this.foldingEnabled.set(this._isEnabled); - if (oldIsEnabled !== this._isEnabled) { - this.onModelChanged(); - } - let oldShowFoldingControls = this._autoHideFoldingControls; - this._autoHideFoldingControls = options.get(EditorOption.showFoldingControls) === 'mouseover'; - if (oldShowFoldingControls !== this._autoHideFoldingControls) { - this.foldingDecorationProvider.autoHideFoldingControls = this._autoHideFoldingControls; - this.onModelContentChanged(); - } - let oldUseFoldingProviders = this._useFoldingProviders; - this._useFoldingProviders = options.get(EditorOption.foldingStrategy) !== 'indentation'; - if (oldUseFoldingProviders !== this._useFoldingProviders) { - this.onFoldingStrategyChanged(); - } + this.onModelChanged(); + } + if (e.hasChanged(EditorOption.showFoldingControls) || e.hasChanged(EditorOption.foldingHighlight)) { + const options = this.editor.getOptions(); + this.foldingDecorationProvider.autoHideFoldingControls = options.get(EditorOption.showFoldingControls) === 'mouseover'; + this.foldingDecorationProvider.showFoldingHighlights = options.get(EditorOption.foldingHighlight); + this.onModelContentChanged(); + } + if (e.hasChanged(EditorOption.foldingStrategy)) { + 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(); @@ -367,7 +368,7 @@ export class FoldingController extends Disposable implements IEditorContribution iconClicked = true; break; case MouseTargetType.CONTENT_EMPTY: { - if (this.hiddenRangeModel.hasRanges()) { + if (this._unfoldOnClickAfterEndOfLine && this.hiddenRangeModel.hasRanges()) { const data = e.target.detail as IEmptyContentData; if (!data.isAfterLines) { break; @@ -421,9 +422,18 @@ export class FoldingController extends Disposable implements IEditorContribution if (region && region.startLineNumber === lineNumber) { let isCollapsed = region.isCollapsed; if (iconClicked || isCollapsed) { - let toToggle = [region]; - if (e.event.middleButton || e.event.shiftKey) { - toToggle.push(...foldingModel.getRegionsInside(region, r => r.isCollapsed === isCollapsed)); + let toToggle = []; + let recursive = e.event.middleButton || e.event.shiftKey; + if (recursive) { + for (const r of foldingModel.getRegionsInside(region)) { + if (r.isCollapsed === isCollapsed) { + toToggle.push(r); + } + } + } + // when recursive, first only collapse all children. If all are already folded or there are no children, also fold parent. + if (isCollapsed || !recursive || toToggle.length === 0) { + toToggle.push(region); } foldingModel.toggleCollapseState(toToggle); this.reveal({ lineNumber, column: 1 }); @@ -610,9 +620,10 @@ class FoldAction extends FoldingAction { { name: 'Fold editor argument', description: `Property-value pairs that can be passed through this argument: - * 'levels': Number of levels to fold. Defaults to 1. + * 'levels': Number of levels to fold. * 'direction': If 'up', folds given number of levels up otherwise folds down. * 'selectionLines': The start lines (0-based) of the editor selections to apply the fold action to. If not set, the active selection(s) will be used. + If no levels or direction is set, folds the region at the locations or if already collapsed, the first uncollapsed parent instead. `, constraint: foldingArgumentsConstraint, schema: { @@ -620,12 +631,10 @@ class FoldAction extends FoldingAction { 'properties': { 'levels': { 'type': 'number', - 'default': 1 }, 'direction': { 'type': 'string', 'enum': ['up', 'down'], - 'default': 'down' }, 'selectionLines': { 'type': 'array', @@ -642,12 +651,20 @@ class FoldAction extends FoldingAction { } invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor, args: FoldingArguments): void { - let levels = args && args.levels || 1; let lineNumbers = this.getLineNumbers(args, editor); - if (args && args.direction === 'up') { - setCollapseStateLevelsUp(foldingModel, true, levels, lineNumbers); + + const levels = args && args.levels; + const direction = args && args.direction; + + if (typeof levels !== 'number' && typeof direction !== 'string') { + // fold the region at the location or if already collapsed, the first uncollapsed parent instead. + setCollapseStateUp(foldingModel, true, lineNumbers); } else { - setCollapseStateLevelsDown(foldingModel, true, levels, lineNumbers); + if (direction === 'up') { + setCollapseStateLevelsUp(foldingModel, true, levels || 1, lineNumbers); + } else { + setCollapseStateLevelsDown(foldingModel, true, levels || 1, lineNumbers); + } } } } @@ -879,3 +896,23 @@ 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('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 333ad459db090..c34e2c2121c23 100644 --- a/src/vs/editor/contrib/folding/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/foldingDecorations.ts @@ -7,33 +7,57 @@ 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({ + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + afterContentClassName: 'inline-folded', + className: 'folded-background', + isWholeLine: true, + 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; + public showFoldingHighlights: boolean = true; + 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 FoldingDecorationProvider.COLLAPSED_VISUAL_DECORATION; + return this.showFoldingHighlights ? FoldingDecorationProvider.COLLAPSED_HIGHLIGHTED_VISUAL_DECORATION : FoldingDecorationProvider.COLLAPSED_VISUAL_DECORATION; } else if (this.autoHideFoldingControls) { return FoldingDecorationProvider.EXPANDED_AUTO_HIDE_VISUAL_DECORATION; } else { diff --git a/src/vs/editor/contrib/folding/foldingModel.ts b/src/vs/editor/contrib/folding/foldingModel.ts index 44feb79cdf23a..a9b7af52657c2 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,22 +99,28 @@ 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, 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 = () => { while (i < this._regions.length) { @@ -204,7 +234,7 @@ export class FoldingModel { return null; } - getRegionsInside(region: FoldingRegion | null, filter?: (r: FoldingRegion, level?: number) => boolean): FoldingRegion[] { + getRegionsInside(region: FoldingRegion | null, filter?: RegionFilter | RegionFilterWithLevel): FoldingRegion[] { let result: FoldingRegion[] = []; let index = region ? region.regionIndex + 1 : 0; let endLineNumber = region ? region.endLineNumber : Number.MAX_VALUE; @@ -229,7 +259,7 @@ export class FoldingModel { for (let i = index, len = this._regions.length; i < len; i++) { let current = this._regions.toRegion(i); if (this._regions.getStartLineNumber(i) < endLineNumber) { - if (!filter || filter(current)) { + if (!filter || (filter as RegionFilter)(current)) { result.push(current); } } else { @@ -242,6 +272,10 @@ export class FoldingModel { } +type RegionFilter = (r: FoldingRegion) => boolean; +type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean; + + /** * Collapse or expand the regions at the given locations * @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels. @@ -270,7 +304,7 @@ export function toggleCollapseState(foldingModel: FoldingModel, levels: number, * @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels. * @param lineNumbers the location of the regions to collapse or expand, or if not set, all regions in the model. */ -export function setCollapseStateLevelsDown(foldingModel: FoldingModel, doCollapse: boolean, levels = Number.MAX_VALUE, lineNumbers?: number[]) { +export function setCollapseStateLevelsDown(foldingModel: FoldingModel, doCollapse: boolean, levels = Number.MAX_VALUE, lineNumbers?: number[]): void { let toToggle: FoldingRegion[] = []; if (lineNumbers && lineNumbers.length > 0) { for (let lineNumber of lineNumbers) { @@ -296,9 +330,9 @@ export function setCollapseStateLevelsDown(foldingModel: FoldingModel, doCollaps * Collapse or expand the regions at the given locations including all parents. * @param doCollapse Wheter to collase or expand * @param levels The number of levels. Use 1 to only impact the regions at the location, use Number.MAX_VALUE for all levels. - * @param lineNumbers the location of the regions to collapse or expand, or if not set, all regions in the model. + * @param lineNumbers the location of the regions to collapse or expand. */ -export function setCollapseStateLevelsUp(foldingModel: FoldingModel, doCollapse: boolean, levels: number, lineNumbers: number[]) { +export function setCollapseStateLevelsUp(foldingModel: FoldingModel, doCollapse: boolean, levels: number, lineNumbers: number[]): void { let toToggle: FoldingRegion[] = []; for (let lineNumber of lineNumbers) { let regions = foldingModel.getAllRegionsAtLine(lineNumber, (region, level) => region.isCollapsed !== doCollapse && level <= levels); @@ -307,6 +341,22 @@ export function setCollapseStateLevelsUp(foldingModel: FoldingModel, doCollapse: foldingModel.toggleCollapseState(toToggle); } +/** + * Collapse or expand a region at the given locations. If the inner most region is already collapsed/expanded, uses the first parent instead. + * @param doCollapse Wheter to collase or expand + * @param lineNumbers the location of the regions to collapse or expand. + */ +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); + if (regions.length > 0) { + toToggle.push(regions[0]); + } + } + foldingModel.toggleCollapseState(toToggle); +} + /** * Folds or unfolds all regions that have a given level, except if they contain one of the blocked lines. * @param foldLevel level. Level == 1 is the top level diff --git a/src/vs/editor/contrib/folding/foldingRanges.ts b/src/vs/editor/contrib/folding/foldingRanges.ts index 1c2e0d1fc7469..bf2a6730a0c61 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/indentRangeProvider.ts b/src/vs/editor/contrib/folding/indentRangeProvider.ts index 68a3a366faad9..da1c2bc7b4c52 100644 --- a/src/vs/editor/contrib/folding/indentRangeProvider.ts +++ b/src/vs/editor/contrib/folding/indentRangeProvider.ts @@ -66,7 +66,7 @@ export class RangesCollector { // reverse and create arrays of the exact length let startIndexes = new Uint32Array(this._length); let endIndexes = new Uint32Array(this._length); - for (let i = this._length - 1, k = 0; i >= 0; i-- , k++) { + for (let i = this._length - 1, k = 0; i >= 0; i--, k++) { startIndexes[k] = this._startIndexes[i]; endIndexes[k] = this._endIndexes[i]; } diff --git a/src/vs/editor/contrib/folding/test/foldingModel.test.ts b/src/vs/editor/contrib/folding/test/foldingModel.test.ts index 00bc4241abc4f..a646fa78bd6c9 100644 --- a/src/vs/editor/contrib/folding/test/foldingModel.test.ts +++ b/src/vs/editor/contrib/folding/test/foldingModel.test.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines } from 'vs/editor/contrib/folding/foldingModel'; -import { TextModel, ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel'; +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)); @@ -587,6 +633,50 @@ suite('Folding Model', () => { }); + test('setCollapseStateUp', () => { + let lines = [ + /* 1*/ '//#region', + /* 2*/ '//#endregion', + /* 3*/ 'class A {', + /* 4*/ ' void foo() {', + /* 5*/ ' if (true) {', + /* 6*/ ' return;', + /* 7*/ ' }', + /* 8*/ '', + /* 9*/ ' if (true) {', + /* 10*/ ' return;', + /* 11*/ ' }', + /* 12*/ ' }', + /* 13*/ '}']; + + let textModel = createTextModel(lines.join('\n')); + try { + let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); + + let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ }); + foldingModel.update(ranges); + + let r1 = r(1, 2, false); + let r2 = r(3, 12, false); + let r3 = r(4, 11, false); + let r4 = r(5, 6, false); + let r5 = r(9, 10, false); + assertRanges(foldingModel, [r1, r2, r3, r4, r5]); + + setCollapseStateUp(foldingModel, true, [5]); + assertFoldedRanges(foldingModel, [r4], '1'); + + setCollapseStateUp(foldingModel, true, [5]); + assertFoldedRanges(foldingModel, [r3, r4], '2'); + + setCollapseStateUp(foldingModel, true, [4]); + assertFoldedRanges(foldingModel, [r2, r3, r4], '2'); + } finally { + textModel.dispose(); + } + + }); + test('setCollapseStateForMatchingLines', () => { let lines = [ @@ -604,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)); @@ -627,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 dd572ec9850e8..214135a9c8cb3 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 18fa5acd3fe8d..9db5f45e4fb20 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 ed51ec95bac4d..ccdb26a756ee2 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 07e2025666796..8a4dbe7f38a24 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 480b84e5d7cdd..86a4280259eb9 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,7 +69,7 @@ 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)]; diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index cdf9ba12223f5..db2d59adaf631 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ 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'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource, TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; import { IActiveCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { registerLanguageCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; 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 * as editorCommon from 'vs/editor/common/editorCommon'; +import { ScrollType } from 'vs/editor/common/editorCommon'; import { ISingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { DocumentFormattingEditProvider, DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProvider, DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry, TextEdit } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; @@ -25,6 +25,10 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; 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 { @@ -108,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 { @@ -129,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); @@ -146,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(); + 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 (!edits || edits.length === 0) { + if (allEdits.length === 0) { return false; } if (isCodeEditor(editorOrModel)) { // use editor to apply edits - FormattingEdit.execute(editorOrModel, edits); - alertFormattingEdits(edits); - editorOrModel.pushUndoStop(); - editorOrModel.focus(); - editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), editorCommon.ScrollType.Immediate); + 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), @@ -209,6 +227,7 @@ export async function formatDocumentWithSelectedProvider( accessor: ServicesAccessor, editorOrModel: ITextModel | IActiveCodeEditor, mode: FormattingMode, + progress: IProgress, token: CancellationToken ): Promise { @@ -217,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); } } @@ -234,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); @@ -264,13 +284,11 @@ 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(), editorCommon.ScrollType.Immediate); + editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), ScrollType.Immediate); } } else { @@ -354,11 +372,11 @@ export function getOnTypeFormattingEdits( }); } -registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) { - const { resource, range, options } = args; - if (!(resource instanceof URI) || !Range.isIRange(range)) { - throw illegalArgument(); - } +CommandsRegistry.registerCommand('_executeFormatRangeProvider', function (accessor, ...args) { + const [resource, range, options] = args; + assertType(URI.isUri(resource)); + assertType(Range.isIRange(range)); + const model = accessor.get(IModelService).getModel(resource); if (!model) { throw illegalArgument('resource'); @@ -366,11 +384,10 @@ registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) return getDocumentRangeFormattingEditsUntilResult(accessor.get(IEditorWorkerService), model, Range.lift(range), options, CancellationToken.None); }); -registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) { - const { resource, options } = args; - if (!(resource instanceof URI)) { - throw illegalArgument('resource'); - } +CommandsRegistry.registerCommand('_executeFormatDocumentProvider', function (accessor, ...args) { + const [resource, options] = args; + assertType(URI.isUri(resource)); + const model = accessor.get(IModelService).getModel(resource); if (!model) { throw illegalArgument('resource'); @@ -379,11 +396,12 @@ registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, ar return getDocumentFormattingEditsUntilResult(accessor.get(IEditorWorkerService), model, options, CancellationToken.None); }); -registerLanguageCommand('_executeFormatOnTypeProvider', function (accessor, args) { - const { resource, position, ch, options } = args; - if (!(resource instanceof URI) || !Position.isIPosition(position) || typeof ch !== 'string') { - throw illegalArgument(); - } +CommandsRegistry.registerCommand('_executeFormatOnTypeProvider', function (accessor, ...args) { + const [resource, position, ch, options] = args; + assertType(URI.isUri(resource)); + assertType(Position.isIPosition(position)); + assertType(typeof ch === 'string'); + const model = accessor.get(IModelService).getModel(resource); if (!model) { throw illegalArgument('resource'); diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index daded6e3e9d18..bedb20cdd9776 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -12,11 +12,11 @@ import { EditorAction, registerEditorAction, registerEditorContribution, Service import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +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,8 +25,9 @@ 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 editorCommon.IEditorContribution { +class FormatOnType implements IEditorContribution { public static readonly ID = 'editor.contrib.autoFormat'; @@ -138,7 +139,7 @@ class FormatOnType implements editorCommon.IEditorContribution { } if (isNonEmptyArray(edits)) { - FormattingEdit.execute(this._editor, edits); + FormattingEdit.execute(this._editor, edits, true); alertFormattingEdits(edits); } @@ -149,7 +150,7 @@ class FormatOnType implements editorCommon.IEditorContribution { } } -class FormatOnPaste implements editorCommon.IEditorContribution { +class FormatOnPaste implements IEditorContribution { public static readonly ID = 'editor.contrib.formatOnPaste'; @@ -191,7 +192,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution { return; } - this._callOnModel.add(this.editor.onDidPaste(range => this._trigger(range))); + this._callOnModel.add(this.editor.onDidPaste(({ range }) => this._trigger(range))); } private _trigger(range: Range): void { @@ -201,7 +202,7 @@ class FormatOnPaste implements editorCommon.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,14 +213,14 @@ 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, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { when: EditorContextKeys.hasDocumentFormattingProvider, group: '1_modification', order: 1.3 @@ -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 + ); } } } @@ -248,7 +253,7 @@ class FormatSelectionAction extends EditorAction { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { when: ContextKeyExpr.and(EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), group: '1_modification', order: 1.31 @@ -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 27dbbd0676231..cabb0a73d7a57 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 10cff5b6510f5..001ed3f273b53 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -4,414 +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 * as editorCommon from 'vs/editor/common/editorCommon'; +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 { 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'; -class MarkerModel { +export class MarkerController implements IEditorContribution { - 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; - } + static readonly ID = 'editor.contrib.markerController'; - 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(); - } -} - -export class MarkerController implements editorCommon.IEditorContribution { - - public 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 + @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; } + let reusePosition = false; + if (this._model) { + reusePosition = true; + this._cleanUp(); + } - 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(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); } }), - 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); } }) - ]; - this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService); + 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._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); - } - 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._sessionDispoables.add(this._model); + this._sessionDispoables.add(this._widget); - 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); - }); + // 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; - } - - private _onMarkerChanged(changedResources: URI[]): void { - const editorModel = this._editor.getModel(); - if (!editorModel) { - return; - } - - if (!this._model) { - return; - } - - if (!changedResources.some(r => isEqual(editorModel.uri, r))) { - return; + 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); + } } - this._model.setMarkers(this._getMarkers()); } - private _getMarkers(): IMarker[] { - let model = this._editor.getModel(); - if (!model) { - 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 (otherEditor) { + MarkerController.get(otherEditor).close(); + MarkerController.get(otherEditor).nagivate(next, multiFile); + } - return this._markerService.read({ - resource: model.uri, - severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info - }); + } else { + // show in this editor + this._widget!.showAtMarker(model.selected.marker, model.selected.index, model.selected.total); + } + } } } 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); - } - - const model = controller.getOrCreateModel(); - const atEdge = model.move(this._isNext, !this._multiFile); - if (!atEdge || !this._multiFile) { - return Promise.resolve(undefined); + async run(_accessor: ServicesAccessor, editor: ICodeEditor): Promise { + if (editor.hasModel()) { + MarkerController.get(editor).nagivate(this._next, this._multiFile); } - - // 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; } } @@ -423,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.editorTextFocus, 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 + } }); } } @@ -437,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.editorTextFocus, 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 + } }); } } @@ -449,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 } }); } @@ -465,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 } }); } @@ -488,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, @@ -496,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 224b24b56e9be..66bdc31f06c54 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'; @@ -26,6 +25,11 @@ import { IAction } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; 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 { 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 { @@ -39,16 +43,23 @@ class MessageWidget { private readonly _relatedDiagnostics = new WeakMap(); private readonly _disposables: DisposableStore = new DisposableStore(); - constructor(parent: HTMLElement, editor: ICodeEditor, onRelatedInformation: (related: IRelatedInformation) => void) { + private _codeLink?: HTMLElement; + + constructor( + parent: HTMLElement, + editor: ICodeEditor, + onRelatedInformation: (related: IRelatedInformation) => void, + private readonly _openerService: IOpenerService, + ) { 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'); @@ -80,16 +91,26 @@ class MessageWidget { 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') { + sourceAndCodeLength += code.length; + } else { + sourceAndCodeLength += code.value.length; + } + } const lines = message.split(/\r\n|\r|\n/g); this._lines = lines.length; this._longestLineLength = 0; for (const line of lines) { - this._longestLineLength = Math.max(line.length, this._longestLineLength); + this._longestLineLength = Math.max(line.length + sourceAndCodeLength, this._longestLineLength); } 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) { @@ -102,19 +123,34 @@ 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) { - const codeElement = document.createElement('span'); - codeElement.innerText = `(${code})`; - dom.addClass(codeElement, 'code'); - detailsElement.appendChild(codeElement); + if (typeof code === 'string') { + const codeElement = document.createElement('span'); + codeElement.innerText = `(${code})`; + codeElement.classList.add('code'); + detailsElement.appendChild(codeElement); + } else { + this._codeLink = dom.$('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, dom.$('span')); + codeElement.innerText = code.value; + detailsElement.appendChild(this._codeLink); + } } } @@ -130,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); @@ -161,10 +197,38 @@ class MessageWidget { getHeightInLines(): number { return Math.min(17, this._lines); } + + 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; + } + + 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; @@ -179,20 +243,23 @@ export class MarkerNavigationWidget extends PeekViewWidget { constructor( editor: ICodeEditor, - private readonly actions: ReadonlyArray, - private readonly _themeService: IThemeService + @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) { @@ -228,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 }); + + 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 { @@ -237,24 +311,25 @@ export class MarkerNavigationWidget extends PeekViewWidget { protected _getActionBarOptions(): IActionBarOptions { return { - orientation: ActionsOrientation.HORIZONTAL_REVERSE + ...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._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'); } @@ -267,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); @@ -284,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 { @@ -329,8 +405,9 @@ export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationI export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.')); registerThemingParticipant((theme, collector) => { - const link = theme.getColor(textLinkForeground); - if (link) { - collector.addRule(`.monaco-editor .marker-widget a { color: ${link}; }`); + const linkFg = theme.getColor(textLinkForeground); + if (linkFg) { + collector.addRule(`.monaco-editor .marker-widget a { color: ${linkFg}; }`); + collector.addRule(`.monaco-editor .marker-widget a.code-link span:hover { color: ${linkFg}; }`); } }); diff --git a/src/vs/editor/contrib/gotoError/markerNavigationService.ts b/src/vs/editor/contrib/gotoError/markerNavigationService.ts new file mode 100644 index 0000000000000..abf0fe7a4e08c --- /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 ee02594a087c5..bf9d507df0ad3 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 { @@ -45,10 +45,27 @@ } .monaco-editor .marker-widget .descriptioncontainer .message .source, -.monaco-editor .marker-widget .descriptioncontainer .message .code { +.monaco-editor .marker-widget .descriptioncontainer .message span.code { opacity: 0.6; } +.monaco-editor .marker-widget .descriptioncontainer .message a.code-link { + opacity: 0.6; + color: inherit; +} +.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:before { + content: '('; +} +.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:after { + content: ')'; +} +.monaco-editor .marker-widget .descriptioncontainer .message a.code-link > span { + text-decoration: underline; + /** Hack to force underline to show **/ + border-bottom: 1px solid transparent; + text-underline-position: under; +} + .monaco-editor .marker-widget .descriptioncontainer .filename { cursor: pointer; } diff --git a/src/vs/editor/contrib/gotoSymbol/documentSymbols.ts b/src/vs/editor/contrib/gotoSymbol/documentSymbols.ts new file mode 100644 index 0000000000000..51702b59e9222 --- /dev/null +++ b/src/vs/editor/contrib/gotoSymbol/documentSymbols.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 { URI } from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { DocumentSymbol } from 'vs/editor/common/modes'; +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 { 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 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 (token.isCancellationRequested) { + return flatEntries; + } + if (flat) { + flatten(flatEntries, roots, ''); + } else { + flatEntries = roots; + } + + return flatEntries.sort(compareEntriesUsingStart); +} + +function compareEntriesUsingStart(a: DocumentSymbol, b: DocumentSymbol): number { + return Range.compareRangesUsingStarts(a.range, b.range); +} + +function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void { + for (let 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... + }); + if (entry.children) { + flatten(bucket, entry.children, entry.name); + } + } +} + +CommandsRegistry.registerCommand('_executeDocumentSymbolProvider', async function (accessor, ...args) { + const [resource] = args; + assertType(URI.isUri(resource)); + + const model = accessor.get(IModelService).getModel(resource); + if (model) { + return getDocumentSymbols(model, false, CancellationToken.None); + } + + const reference = await accessor.get(ITextModelService).createModelReference(resource); + try { + return await getDocumentSymbols(reference.object.textEditorModel, false, CancellationToken.None); + } finally { + reference.dispose(); + } +}); diff --git a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts index f1d81f9bd57e4..232aab8d0f896 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToCommands.ts @@ -21,21 +21,31 @@ import { PeekContext } from 'vs/editor/contrib/peekView/peekView'; import { ReferencesController } from 'vs/editor/contrib/gotoSymbol/peek/referencesController'; import { ReferencesModel } from 'vs/editor/contrib/gotoSymbol/referencesModel'; import * as nls from 'vs/nls'; -import { MenuId } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, ISubmenuItem } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition, getReferencesAtPosition } from './goToSymbol'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { EditorStateCancellationTokenSource, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; import { ISymbolNavigationService } from 'vs/editor/contrib/gotoSymbol/symbolNavigation'; import { EditorOption, GoToLocationValues } from 'vs/editor/common/config/editorOptions'; import { isStandalone } from 'vs/base/browser/browser'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ScrollType } from 'vs/editor/common/editorCommon'; +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, { + submenu: MenuId.EditorContextPeek, + title: nls.localize('peek.submenu', "Peek"), + group: 'navigation', + order: 100 +}); export interface SymbolNavigationActionConfig { openToSide: boolean; @@ -74,8 +84,15 @@ abstract class SymbolNavigationAction extends EditorAction { alert(references.ariaMessage); + let altAction: IEditorAction | null | undefined; + if (references.referenceAt(model.uri, pos)) { + const altActionId = this._getAlternativeCommand(editor); + if (altActionId !== this.id) { + altAction = editor.getAction(altActionId); + } + } + const referenceCount = references.references.length; - const altAction = references.referenceAt(model.uri, pos) && editor.getAction(this._getAlternativeCommand()); if (referenceCount === 0) { // no result -> show message @@ -107,22 +124,21 @@ abstract class SymbolNavigationAction extends EditorAction { protected abstract _getNoResultFoundMessage(info: IWordAtPosition | null): string; - protected abstract _getMetaTitle(model: ReferencesModel): string; - - protected abstract _getAlternativeCommand(): string; + protected abstract _getAlternativeCommand(editor: IActiveCodeEditor): string; protected abstract _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues; 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 { const next = model.firstReference()!; - const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide); - if (targetEditor && model.references.length > 1 && gotoLocation === 'gotoAndPeek') { + const peek = model.references.length > 1 && gotoLocation === 'gotoAndPeek'; + const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide, !peek); + if (peek && targetEditor) { this._openInPeek(targetEditor, model); } else { model.dispose(); @@ -136,9 +152,9 @@ abstract class SymbolNavigationAction extends EditorAction { } } - private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise { + private async _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean, highlight: boolean): Promise { // range is the target-selection-range when we have one - // and the the fallback is the 'full' range + // and the fallback is the 'full' range let range: IRange | undefined = undefined; if (isLocationLink(reference)) { range = reference.targetSelectionRange; @@ -147,13 +163,29 @@ abstract class SymbolNavigationAction extends EditorAction { range = reference.range; } - return editorService.openCodeEditor({ + const targetEditor = await editorService.openCodeEditor({ resource: reference.uri, options: { selection: Range.collapseToStart(range), - revealInCenterIfOutsideViewport: true + selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport } }, editor, sideBySide); + + if (!targetEditor) { + return undefined; + } + + if (highlight) { + const modelNow = targetEditor.getModel(); + const ids = targetEditor.deltaDecorations([], [{ range, options: { className: 'symbolHighlight' } }]); + setTimeout(() => { + if (targetEditor.getModel() === modelNow) { + targetEditor.deltaDecorations(ids, []); + } + }, 350); + } + + return targetEditor; } private _openInPeek(target: ICodeEditor, model: ReferencesModel) { @@ -171,7 +203,7 @@ abstract class SymbolNavigationAction extends EditorAction { export class DefinitionAction extends SymbolNavigationAction { protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { - return new ReferencesModel(await getDefinitionsAtPosition(model, position, token)); + return new ReferencesModel(await getDefinitionsAtPosition(model, position, token), nls.localize('def.title', 'Definitions')); } protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { @@ -180,12 +212,8 @@ export class DefinitionAction extends SymbolNavigationAction { : nls.localize('generic.noResults', "No definition found"); } - protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 ? nls.localize('meta.title', " – {0} definitions", model.references.length) : ''; - } - - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeDefinitionCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { @@ -212,17 +240,17 @@ 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, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.1 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 2, @@ -248,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), @@ -275,13 +303,18 @@ registerEditorAction(class PeekDefinitionAction extends DefinitionAction { precondition: ContextKeyExpr.and( EditorContextKeys.hasDefinitionProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F12, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 }, weight: KeybindingWeight.EditorContrib + }, + contextMenuOpts: { + menuId: MenuId.EditorContextPeek, + group: 'peek', + order: 2 } }); CommandsRegistry.registerCommandAlias('editor.action.previewDeclaration', PeekDefinitionAction.id); @@ -295,7 +328,7 @@ registerEditorAction(class PeekDefinitionAction extends DefinitionAction { class DeclarationAction extends SymbolNavigationAction { protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { - return new ReferencesModel(await getDeclarationsAtPosition(model, position, token)); + return new ReferencesModel(await getDeclarationsAtPosition(model, position, token), nls.localize('decl.title', 'Declarations')); } protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { @@ -304,12 +337,8 @@ class DeclarationAction extends SymbolNavigationAction { : nls.localize('decl.generic.noResults', "No declaration found"); } - protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 ? nls.localize('decl.meta.title', " – {0} declarations", model.references.length) : ''; - } - - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeDeclarationCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { @@ -332,13 +361,13 @@ registerEditorAction(class GoToDeclarationAction extends DeclarationAction { alias: 'Go to Declaration', precondition: ContextKeyExpr.and( EditorContextKeys.hasDeclarationProvider, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.3 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 3, @@ -352,10 +381,6 @@ registerEditorAction(class GoToDeclarationAction extends DeclarationAction { ? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word) : nls.localize('decl.generic.noResults', "No declaration found"); } - - protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 ? nls.localize('decl.meta.title', " – {0} declarations", model.references.length) : ''; - } }); registerEditorAction(class PeekDeclarationAction extends DeclarationAction { @@ -371,8 +396,13 @@ registerEditorAction(class PeekDeclarationAction extends DeclarationAction { precondition: ContextKeyExpr.and( EditorContextKeys.hasDeclarationProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), + contextMenuOpts: { + menuId: MenuId.EditorContextPeek, + group: 'peek', + order: 3 + } }); } }); @@ -384,7 +414,7 @@ registerEditorAction(class PeekDeclarationAction extends DeclarationAction { class TypeDefinitionAction extends SymbolNavigationAction { protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { - return new ReferencesModel(await getTypeDefinitionsAtPosition(model, position, token)); + return new ReferencesModel(await getTypeDefinitionsAtPosition(model, position, token), nls.localize('typedef.title', 'Type Definitions')); } protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { @@ -393,12 +423,8 @@ class TypeDefinitionAction extends SymbolNavigationAction { : nls.localize('goToTypeDefinition.generic.noResults', "No type definition found"); } - protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 ? nls.localize('meta.typeDefinitions.title', " – {0} type definitions", model.references.length) : ''; - } - - protected _getAlternativeCommand(): string { - return 'editor.action.goToReferences'; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeTypeDefinitionCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { @@ -421,17 +447,17 @@ 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, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.4 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 3, @@ -457,8 +483,13 @@ registerEditorAction(class PeekTypeDefinitionAction extends TypeDefinitionAction precondition: ContextKeyExpr.and( EditorContextKeys.hasTypeDefinitionProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() - ) + EditorContextKeys.isInWalkThroughSnippet.toNegated() + ), + contextMenuOpts: { + menuId: MenuId.EditorContextPeek, + group: 'peek', + order: 4 + } }); } }); @@ -470,7 +501,7 @@ registerEditorAction(class PeekTypeDefinitionAction extends TypeDefinitionAction class ImplementationAction extends SymbolNavigationAction { protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { - return new ReferencesModel(await getImplementationsAtPosition(model, position, token)); + return new ReferencesModel(await getImplementationsAtPosition(model, position, token), nls.localize('impl.title', 'Implementations')); } protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { @@ -479,16 +510,12 @@ class ImplementationAction extends SymbolNavigationAction { : nls.localize('goToImplementation.generic.noResults', "No implementation found"); } - protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 ? nls.localize('meta.implementations.title', " – {0} implementations", model.references.length) : ''; - } - - protected _getAlternativeCommand(): string { - return ''; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeImplementationCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { - return editor.getOption(EditorOption.gotoLocation).multipleImplemenations; + return editor.getOption(EditorOption.gotoLocation).multipleImplementations; } } @@ -507,19 +534,19 @@ 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, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 4, title: nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementations") }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.45 } @@ -543,12 +570,17 @@ registerEditorAction(class PeekImplementationAction extends ImplementationAction precondition: ContextKeyExpr.and( EditorContextKeys.hasImplementationProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12, weight: KeybindingWeight.EditorContrib + }, + contextMenuOpts: { + menuId: MenuId.EditorContextPeek, + group: 'peek', + order: 5 } }); } @@ -558,11 +590,7 @@ registerEditorAction(class PeekImplementationAction extends ImplementationAction //#region --- REFERENCES -class ReferencesAction extends SymbolNavigationAction { - - protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { - return new ReferencesModel(await getReferencesAtPosition(model, position, token)); - } +abstract class ReferencesAction extends SymbolNavigationAction { protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { return info @@ -570,14 +598,8 @@ class ReferencesAction extends SymbolNavigationAction { : nls.localize('references.noGeneric', "No references found"); } - protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 - ? nls.localize('meta.titleReference', " – {0} references", model.references.length) - : ''; - } - - protected _getAlternativeCommand(): string { - return ''; + protected _getAlternativeCommand(editor: IActiveCodeEditor): string { + return editor.getOption(EditorOption.gotoLocation).alternativeReferenceCommand; } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { @@ -599,18 +621,18 @@ registerEditorAction(class GoToReferencesAction extends ReferencesAction { precondition: ContextKeyExpr.and( EditorContextKeys.hasReferenceProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyCode.F12, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: 'navigation', order: 1.45 }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarGoMenu, group: '4_symbol_nav', order: 5, @@ -618,6 +640,10 @@ registerEditorAction(class GoToReferencesAction extends ReferencesAction { }, }); } + + protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { + return new ReferencesModel(await getReferencesAtPosition(model, position, true, token), nls.localize('ref.title', 'References')); + } }); registerEditorAction(class PeekReferencesAction extends ReferencesAction { @@ -634,10 +660,19 @@ registerEditorAction(class PeekReferencesAction extends ReferencesAction { precondition: ContextKeyExpr.and( EditorContextKeys.hasReferenceProvider, PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() - ) + EditorContextKeys.isInWalkThroughSnippet.toNegated() + ), + contextMenuOpts: { + menuId: MenuId.EditorContextPeek, + group: 'peek', + order: 6 + } }); } + + protected async _getLocationModel(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { + return new ReferencesModel(await getReferencesAtPosition(model, position, false, token), nls.localize('ref.title', 'References')); + } }); //#endregion @@ -648,25 +683,23 @@ registerEditorAction(class PeekReferencesAction extends ReferencesAction { class GenericGoToLocationAction extends SymbolNavigationAction { constructor( - private readonly _references: Location[] + config: SymbolNavigationActionConfig, + private readonly _references: Location[], + private readonly _gotoMultipleBehaviour: GoToLocationValues | undefined, ) { - super({ - muteMessage: true, - openInPeek: false, - openToSide: false - }, { + super(config, { id: 'editor.action.goToLocation', label: nls.localize('label.generic', "Go To Any Symbol"), alias: 'Go To Any Symbol', precondition: ContextKeyExpr.and( PeekContext.notInPeekEditor, - EditorContextKeys.isInEmbeddedEditor.toNegated() + EditorContextKeys.isInWalkThroughSnippet.toNegated() ), }); } protected async _getLocationModel(_model: ITextModel, _position: corePosition.Position, _token: CancellationToken): Promise { - return new ReferencesModel(this._references); + return new ReferencesModel(this._references, nls.localize('generic.title', 'Locations')); } protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { @@ -674,10 +707,9 @@ class GenericGoToLocationAction extends SymbolNavigationAction { } protected _getGoToPreference(editor: IActiveCodeEditor): GoToLocationValues { - return editor.getOption(EditorOption.gotoLocation).multipleReferences; + return this._gotoMultipleBehaviour ?? editor.getOption(EditorOption.gotoLocation).multipleReferences; } - protected _getMetaTitle() { return ''; } protected _getAlternativeCommand() { return ''; } } @@ -689,12 +721,16 @@ CommandsRegistry.registerCommand({ { name: 'uri', description: 'The text document in which to start', constraint: URI }, { 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) => { + 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)); + assertType(typeof multiple === 'undefined' || typeof multiple === 'string'); + assertType(typeof openInPeek === 'undefined' || typeof openInPeek === 'boolean'); const editorService = accessor.get(ICodeEditorService); const editor = await editorService.openCodeEditor({ resource }, editorService.getFocusedCodeEditor()); @@ -704,13 +740,38 @@ CommandsRegistry.registerCommand({ editor.revealPositionInCenterIfOutsideViewport(position, ScrollType.Smooth); return editor.invokeWithinContext(accessor => { - const command = new GenericGoToLocationAction(references); + 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); }); } } }); +CommandsRegistry.registerCommand({ + id: 'editor.action.peekLocations', + description: { + description: 'Peek locations from a position in a file', + args: [ + { name: 'uri', description: 'The text document in which to start', constraint: URI }, + { 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' }, + ] + }, + handler: async (accessor: ServicesAccessor, resource: any, position: any, references: any, multiple?: any) => { + accessor.get(ICommandService).executeCommand('editor.action.goToLocations', resource, position, references, multiple, undefined, true); + } +}); + //#endregion @@ -733,7 +794,7 @@ CommandsRegistry.registerCommand({ return undefined; } - const references = createCancelablePromise(token => getReferencesAtPosition(control.getModel(), corePosition.Position.lift(position), token).then(references => new ReferencesModel(references))); + const references = createCancelablePromise(token => getReferencesAtPosition(control.getModel(), corePosition.Position.lift(position), false, token).then(references => new ReferencesModel(references, nls.localize('ref.title', 'References')))); const range = new Range(position.lineNumber, position.column, position.lineNumber, position.column); return Promise.resolve(controller.toggleWidget(range, references, false)); }); @@ -741,6 +802,6 @@ CommandsRegistry.registerCommand({ }); // use NEW command -CommandsRegistry.registerCommandAlias('editor.action.showReferences', 'editor.action.goToLocations'); +CommandsRegistry.registerCommandAlias('editor.action.showReferences', 'editor.action.peekLocations'); //#endregion diff --git a/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts b/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts index 05a509e7c8c97..e3e7ec687d567 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts @@ -3,10 +3,9 @@ * 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 { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { LocationLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, DeclarationProviderRegistry, ProviderResult, ReferenceProviderRegistry } from 'vs/editor/common/modes'; @@ -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; + }); } @@ -58,10 +66,10 @@ export function getTypeDefinitionsAtPosition(model: ITextModel, position: Positi }); } -export function getReferencesAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { +export function getReferencesAtPosition(model: ITextModel, position: Position, compact: boolean, token: CancellationToken): Promise { return getLocationLinks(model, position, ReferenceProviderRegistry, async (provider, model, position) => { const result = await provider.provideReferences(model, position, { includeDeclaration: true }, token); - if (!result || result.length !== 2) { + if (!compact || !result || result.length !== 2) { return result; } const resultWithoutDeclaration = await provider.provideReferences(model, position, { includeDeclaration: false }, token); @@ -72,8 +80,8 @@ export function getReferencesAtPosition(model: ITextModel, position: Position, t }); } -registerDefaultLanguageCommand('_executeDefinitionProvider', (model, position) => getDefinitionsAtPosition(model, position, CancellationToken.None)); -registerDefaultLanguageCommand('_executeDeclarationProvider', (model, position) => getDeclarationsAtPosition(model, position, CancellationToken.None)); -registerDefaultLanguageCommand('_executeImplementationProvider', (model, position) => getImplementationsAtPosition(model, position, CancellationToken.None)); -registerDefaultLanguageCommand('_executeTypeDefinitionProvider', (model, position) => getTypeDefinitionsAtPosition(model, position, CancellationToken.None)); -registerDefaultLanguageCommand('_executeReferenceProvider', (model, position) => getReferencesAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeDefinitionProvider', (model, position) => getDefinitionsAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeDeclarationProvider', (model, position) => getDeclarationsAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeImplementationProvider', (model, position) => getImplementationsAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeTypeDefinitionProvider', (model, position) => getTypeDefinitionsAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeReferenceProvider', (model, position) => getReferencesAtPosition(model, position, false, CancellationToken.None)); diff --git a/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture.ts b/src/vs/editor/contrib/gotoSymbol/link/clickLinkGesture.ts index 003ed988f84f6..19f2de625f873 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.onDidChangeCursorSelection((e) => this.onDidChangeCursorSelection(e))); - this._register(this._editor.onDidChangeModel((e) => this.resetHandler())); - this._register(this._editor.onDidChangeModelContent(() => 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.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 240bf4c52df2b..68c3275a7a4ab 100644 --- a/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts +++ b/src/vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition.ts @@ -11,7 +11,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { IModeService } from 'vs/editor/common/services/modeService'; import { Range, IRange } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { DefinitionProviderRegistry, LocationLink } from 'vs/editor/common/modes'; import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -27,8 +27,12 @@ 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 editorCommon.IEditorContribution { +export class GotoDefinitionAtPositionEditorContribution implements IEditorContribution { public static readonly ID = 'editor.contrib.gotodefinitionatposition'; static readonly MAX_SOURCE_PREVIEW_LINES = 8; @@ -334,8 +338,16 @@ export class GotoDefinitionAtPositionEditorContribution implements editorCommon. 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 1b5389383e452..b7942a37a72e1 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -5,29 +5,31 @@ 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'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ReferencesModel, OneReference } from '../referencesModel'; -import { ReferenceWidget, LayoutData, ctxReferenceWidgetSearchTreeFocused } from './referencesWidget'; +import { ReferenceWidget, LayoutData } from './referencesWidget'; import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; import { Location } from 'vs/editor/common/modes'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { getOuterEditor, PeekContext } from 'vs/editor/contrib/peekView/peekView'; -import { IListService } from 'vs/platform/list/browser/listService'; +import { IListService, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; export const ctxReferenceSearchVisible = new RawContextKey('referenceSearchVisible', false); -export abstract class ReferencesController implements editorCommon.IEditorContribution { +export abstract class ReferencesController implements IEditorContribution { static readonly ID = 'editor.contrib.referencesController'; @@ -62,8 +64,8 @@ export abstract class ReferencesController implements editorCommon.IEditorContri 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; } @@ -151,10 +153,8 @@ export abstract class ReferencesController implements editorCommon.IEditorContri if (this._widget && this._model && this._editor.hasModel()) { // might have been closed // set title - if (this._model.references.length === 1) { - this._widget.setMetaTitle(nls.localize('metaTitle.1', "1 result")); - } else if (!this._model.isEmpty) { - this._widget.setMetaTitle(nls.localize('metaTitle.N', "{0} results", this._model.references.length)); + if (!this._model.isEmpty) { + this._widget.setMetaTitle(nls.localize('metaTitle.N', "{0} ({1})", this._model.title, this._model.references.length)); } else { this._widget.setMetaTitle(''); } @@ -164,7 +164,11 @@ export abstract class ReferencesController implements editorCommon.IEditorContri let pos = new Position(range.startLineNumber, range.startColumn); let selection = this._model.nearestReference(uri, pos); if (selection) { - return this._widget.setSelection(selection); + return this._widget.setSelection(selection).then(() => { + if (this._widget && this._editor.getOption(EditorOption.peekWidgetDefaultFocus) === 'editor') { + this._widget.focusOnPreviewEditor(); + } + }); } } return undefined; @@ -175,6 +179,18 @@ export abstract class ReferencesController implements editorCommon.IEditorContri }); } + changeFocusBetweenPreviewAndReferences() { + if (!this._widget) { + // can be called while still resolving... + return; + } + if (this._widget.isPreviewEditorFocused()) { + this._widget.focusOnReferenceTree(); + } else { + this._widget.focusOnPreviewEditor(); + } + } + async goToNextOrPreviousReference(fwd: boolean) { if (!this._editor.hasModel() || !this._model || !this._widget) { // can be called while still resolving... @@ -190,21 +206,35 @@ export abstract class ReferencesController implements editorCommon.IEditorContri } const target = this._model.nextOrPreviousReference(source, fwd); const editorFocus = this._editor.hasTextFocus(); + const previewEditorFocus = this._widget.isPreviewEditorFocused(); await this._widget.setSelection(target); await this._gotoReference(target); if (editorFocus) { this._editor.focus(); + } else if (this._widget && previewEditorFocus) { + this._widget.focusOnPreviewEditor(); } } - 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 } @@ -231,7 +261,7 @@ export abstract class ReferencesController implements editorCommon.IEditorContri if (this._editor === openedEditor) { // this._widget.show(range); - this._widget.focus(); + this._widget.focusOnReferenceTree(); } else { // we opened a different editor instance which means a different controller instance. @@ -281,24 +311,23 @@ function withController(accessor: ServicesAccessor, fn: (controller: ReferencesC } KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'goToNextReference', - weight: KeybindingWeight.WorkbenchContrib + 50, - primary: KeyCode.F4, - secondary: [KeyCode.F12], - when: ctxReferenceSearchVisible, + id: 'togglePeekWidgetFocus', + weight: KeybindingWeight.EditorContrib, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.F2), + when: ContextKeyExpr.or(ctxReferenceSearchVisible, PeekContext.inPeekEditor), handler(accessor) { withController(accessor, controller => { - controller.goToNextOrPreviousReference(true); + controller.changeFocusBetweenPreviewAndReferences(); }); } }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'goToNextReferenceFromEmbeddedEditor', - weight: KeybindingWeight.EditorContrib + 50, + id: 'goToNextReference', + weight: KeybindingWeight.EditorContrib - 10, primary: KeyCode.F4, secondary: [KeyCode.F12], - when: PeekContext.inPeekEditor, + when: ContextKeyExpr.or(ctxReferenceSearchVisible, PeekContext.inPeekEditor), handler(accessor) { withController(accessor, controller => { controller.goToNextOrPreviousReference(true); @@ -308,10 +337,10 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'goToPreviousReference', - weight: KeybindingWeight.WorkbenchContrib + 50, + weight: KeybindingWeight.EditorContrib - 10, primary: KeyMod.Shift | KeyCode.F4, secondary: [KeyMod.Shift | KeyCode.F12], - when: ctxReferenceSearchVisible, + when: ContextKeyExpr.or(ctxReferenceSearchVisible, PeekContext.inPeekEditor), handler(accessor) { withController(accessor, controller => { controller.goToNextOrPreviousReference(false); @@ -319,38 +348,47 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'goToPreviousReferenceFromEmbeddedEditor', - weight: KeybindingWeight.EditorContrib + 50, - primary: KeyMod.Shift | KeyCode.F4, - secondary: [KeyMod.Shift | KeyCode.F12], - when: PeekContext.inPeekEditor, - handler(accessor) { - withController(accessor, controller => { - controller.goToNextOrPreviousReference(false); - }); - } +// commands that aren't needed anymore because there is now ContextKeyExpr.OR +CommandsRegistry.registerCommandAlias('goToNextReferenceFromEmbeddedEditor', 'goToNextReference'); +CommandsRegistry.registerCommandAlias('goToPreviousReferenceFromEmbeddedEditor', 'goToPreviousReference'); + +// close +CommandsRegistry.registerCommandAlias('closeReferenceSearchEditor', 'closeReferenceSearch'); +CommandsRegistry.registerCommand( + 'closeReferenceSearch', + accessor => withController(accessor, controller => controller.closeWidget()) +); +KeybindingsRegistry.registerKeybindingRule({ + id: 'closeReferenceSearch', + weight: KeybindingWeight.EditorContrib - 101, + primary: KeyCode.Escape, + secondary: [KeyMod.Shift | KeyCode.Escape], + when: ContextKeyExpr.and(PeekContext.inPeekEditor, ContextKeyExpr.not('config.editor.stablePeek')) }); - -KeybindingsRegistry.registerCommandAndKeybindingRule({ +KeybindingsRegistry.registerKeybindingRule({ id: 'closeReferenceSearch', weight: KeybindingWeight.WorkbenchContrib + 50, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], - when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')), - handler(accessor: ServicesAccessor) { - withController(accessor, controller => controller.closeWidget()); - } + when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')) }); + KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'closeReferenceSearchEditor', - weight: KeybindingWeight.EditorContrib - 101, - primary: KeyCode.Escape, - secondary: [KeyMod.Shift | KeyCode.Escape], - when: ContextKeyExpr.and(PeekContext.inPeekEditor, ContextKeyExpr.not('config.editor.stablePeek')), + id: 'revealReference', + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyCode.Enter, + mac: { + primary: KeyCode.Enter, + secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow] + }, + when: ContextKeyExpr.and(ctxReferenceSearchVisible, WorkbenchListFocusContextKey), handler(accessor: ServicesAccessor) { - withController(accessor, controller => controller.closeWidget()); + 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])); + } } }); @@ -361,12 +399,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ mac: { primary: KeyMod.WinCtrl | KeyCode.Enter }, - when: ContextKeyExpr.and(ctxReferenceSearchVisible, ctxReferenceWidgetSearchTreeFocused), + when: ContextKeyExpr.and(ctxReferenceSearchVisible, WorkbenchListFocusContextKey), handler(accessor: ServicesAccessor) { const listService = accessor.get(IListService); - const focus = listService.lastFocusedList && listService.lastFocusedList.getFocus(); - if (focus instanceof OneReference) { - withController(accessor, controller => controller.openReference(focus, true)); + const focus = listService.lastFocusedList?.getFocus(); + if (Array.isArray(focus) && focus[0] instanceof OneReference) { + withController(accessor, controller => controller.openReference(focus[0], true)); } } }); + +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)); + } +}); diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesTree.ts index 0db277b7cf1f8..f448805d28896 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 { getId(element: TreeElement): { toString(): string; } { - return element.uri; + return element instanceof OneReference ? element.id : element.uri; } } @@ -119,7 +118,7 @@ class FileReferencesTemplate extends Disposable { ) { super(); const parent = document.createElement('div'); - dom.addClass(parent, 'reference-file'); + parent.classList.add('reference-file'); this.file = this._register(new IconLabel(parent, { supportHighlights: true })); this.badge = new CountBadge(dom.append(parent, dom.$('.count'))); @@ -133,9 +132,7 @@ class FileReferencesTemplate extends Disposable { this.file.setLabel(getBaseLabel(element.uri), this._uriLabel.getUriLabel(parent, { relative: true }), { title: this._uriLabel.getUriLabel(element.uri), matches }); const len = element.children.length; this.badge.setCount(len); - if (element.failure) { - this.badge.setTitleFormat(localize('referencesFailre', "Failed to resolve file.")); - } else if (len > 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 00130718f260f..86e708fbdc83c 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -11,29 +11,28 @@ 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'; import { IRange, Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ScrollType } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; 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 { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; -import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; +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 { IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree'; 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'; class DecorationsManager implements IDisposable { @@ -62,12 +61,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; } } } @@ -76,7 +76,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[] = []; @@ -86,6 +86,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 @@ -107,19 +110,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 { @@ -137,7 +142,7 @@ class DecorationsManager implements IDisposable { } else { reference.range = newRange; } - }); + } for (let i = 0, len = toRemove.length; i < len; i++) { this._decorations.delete(toRemove[i]); @@ -146,11 +151,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(); } } @@ -182,7 +183,7 @@ export interface SelectionEvent { readonly element?: Location; } -export const ctxReferenceWidgetSearchTreeFocused = new RawContextKey('referenceSearchTreeFocused', true); +class ReferencesTree extends WorkbenchAsyncDataTree { } /** * ZoneWidget that is shown inside the editor @@ -198,7 +199,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; @@ -216,12 +217,13 @@ 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, ) { - 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(); } @@ -238,7 +240,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, @@ -250,14 +252,22 @@ export class ReferenceWidget extends peekView.PeekViewWidget { } show(where: IRange) { - this.editor.revealRangeInCenterIfOutsideViewport(where, editorCommon.ScrollType.Smooth); + this.editor.revealRangeInCenterIfOutsideViewport(where, ScrollType.Smooth); super.show(where, this.layoutData.heightInLines || 18); } - focus(): void { + focusOnReferenceTree(): void { this._tree.domFocus(); } + focusOnPreviewEditor(): void { + this._preview.focus(); + } + + isPreviewEditorFocused(): boolean { + return this._preview.hasTextFocus(); + } + protected _onTitleClick(e: IMouseEvent): void { if (this._preview && this._preview.getModel()) { this._onDidSelectReference.fire({ @@ -286,7 +296,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget { horizontal: 'auto', useShadows: true, verticalHasArrows: false, - horizontalHasArrows: false + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false }, overviewRulerLanes: 2, fixedOverflowWidgets: true, @@ -296,19 +307,23 @@ 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: IAsyncDataTreeOptions = { - ariaLabel: nls.localize('treeAriaLabel', "References"), + const treeOptions: IWorkbenchAsyncDataTreeOptions = { keyboardSupport: this._defaultTreeKeyboardSupport, - accessibilityProvider: new AriaProvider(), + accessibilityProvider: new AccessibilityProvider(), keyboardNavigationLabelProvider: this._instantiationService.createInstance(StringRepresentationProvider), - identityProvider: new IdentityProvider() + identityProvider: new IdentityProvider(), + openOnSingleClick: true, + openOnFocus: true, + overrideStyles: { + listBackground: peekView.peekViewResultsBackground + } }; - this._tree = this._instantiationService.createInstance>( - WorkbenchAsyncDataTree, + this._tree = this._instantiationService.createInstance( + ReferencesTree, 'ReferencesWidget', this._treeContainer, new Delegate(), @@ -317,9 +332,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget { this._instantiationService.createInstance(OneReferenceRenderer), ], this._instantiationService.createInstance(DataSource), - treeOptions + treeOptions, ); - ctxReferenceWidgetSearchTreeFocused.bindTo(this._tree.contextKeyService); // split stuff this._splitView.addView({ @@ -359,19 +373,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)) { - // keybinding (list service command) OR double click -> 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'); } }); @@ -421,7 +429,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); } @@ -451,11 +459,11 @@ 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); - this.focus(); + this.focusOnReferenceTree(); // pick input and a reference to begin with return this._tree.setInput(this._model.groups.length === 1 ? this._model.groups[0] : this._model); @@ -473,6 +481,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 { @@ -515,7 +528,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { // show in editor const model = ref.object; if (model) { - const scrollType = this._preview.getModel() === model.textEditorModel ? editorCommon.ScrollType.Smooth : editorCommon.ScrollType.Immediate; + const scrollType = this._preview.getModel() === model.textEditorModel ? ScrollType.Smooth : ScrollType.Immediate; const sel = Range.lift(reference.range).collapseToStart(); this._previewModelReference = ref; this._preview.setModel(model.textEditorModel); diff --git a/src/vs/editor/contrib/gotoSymbol/referencesModel.ts b/src/vs/editor/contrib/gotoSymbol/referencesModel.ts index 77b09ed7b0f8a..5c4bbe4d02106 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; - } - - get preview(): FilePreview | undefined { - return this._preview; + dispose(this._previews.values()); + this._previews.clear(); } - 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; } } @@ -149,6 +133,7 @@ export class ReferencesModel implements IDisposable { private readonly _disposables = new DisposableStore(); private readonly _links: LocationLink[]; + private readonly _title: string; readonly groups: FileReferences[] = []; readonly references: OneReference[] = []; @@ -156,8 +141,9 @@ export class ReferencesModel implements IDisposable { readonly _onDidChangeReferenceRange = new Emitter(); readonly onDidChangeReferenceRange: Event = this._onDidChangeReferenceRange.event; - constructor(links: LocationLink[]) { + constructor(links: LocationLink[], title: string) { this._links = links; + this._title = title; // grouping and sorting const [providersFirst] = links; @@ -165,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); @@ -192,7 +181,11 @@ export class ReferencesModel implements IDisposable { } clone(): ReferencesModel { - return new ReferencesModel(this._links); + return new ReferencesModel(this._links, this._title); + } + + get title(): string { + return this._title; } get isEmpty(): boolean { @@ -288,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 9d9bc68a5588f..915aae90522eb 100644 --- a/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts +++ b/src/vs/editor/contrib/gotoSymbol/symbolNavigation.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ReferencesModel, OneReference } from 'vs/editor/contrib/gotoSymbol/referencesModel'; -import { RawContextKey, IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -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 @@ -155,10 +156,7 @@ registerEditorCommand(new class extends EditorCommand { constructor() { super({ id: 'editor.gotoNextSymbolFromResult', - precondition: ContextKeyExpr.and( - ctxHasSymbols, - ContextKeyExpr.equals('config.editor.gotoLocation.multiple', 'goto') - ), + precondition: ctxHasSymbols, kbOpts: { weight: KeybindingWeight.EditorContrib, primary: KeyCode.F12 @@ -200,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 { @@ -211,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/gotoSymbol/test/referencesModel.test.ts b/src/vs/editor/contrib/gotoSymbol/test/referencesModel.test.ts index f3a0d01191412..c2a1b95246a99 100644 --- a/src/vs/editor/contrib/gotoSymbol/test/referencesModel.test.ts +++ b/src/vs/editor/contrib/gotoSymbol/test/referencesModel.test.ts @@ -21,7 +21,7 @@ suite('references', function () { }, { uri: URI.file('/src/can'), range: new Range(1, 1, 1, 1) - }]); + }], 'FOO'); let ref = model.nearestReference(URI.file('/src/can'), new Position(1, 1)); assert.equal(ref!.uri.path, '/src/can'); diff --git a/src/vs/editor/contrib/hover/getHover.ts b/src/vs/editor/contrib/hover/getHover.ts index d6adae27bdf1c..db914a798ae8c 100644 --- a/src/vs/editor/contrib/hover/getHover.ts +++ b/src/vs/editor/contrib/hover/getHover.ts @@ -6,7 +6,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes'; @@ -27,7 +27,7 @@ export function getHover(model: ITextModel, position: Position, token: Cancellat return Promise.all(promises).then(coalesce); } -registerDefaultLanguageCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None)); function isValid(result: Hover) { const hasRange = (typeof result.range !== 'undefined'); diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/editor/contrib/hover/hover.css deleted file mode 100644 index 3e34dec5e19de..0000000000000 --- a/src/vs/editor/contrib/hover/hover.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. - *--------------------------------------------------------------------------------------------*/ - -.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 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; -} diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 3a0f4eea25228..01b4416ffc54c 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,6 +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 { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; export class ModesHoverController implements IEditorContribution { @@ -57,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); } @@ -66,7 +68,8 @@ export class ModesHoverController implements IEditorContribution { @IModeService private readonly _modeService: IModeService, @IMarkerDecorationsService private readonly _markerDecorationsService: IMarkerDecorationsService, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IThemeService private readonly _themeService: IThemeService + @IThemeService private readonly _themeService: IThemeService, + @IContextKeyService _contextKeyService: IContextKeyService ) { this._isMouseDown = false; this._hoverClicked = false; @@ -80,6 +83,8 @@ export class ModesHoverController implements IEditorContribution { this._hookEvents(); } }); + + this._hoverVisibleKey = EditorContextKeys.hoverVisible.bindTo(_contextKeyService); } private _hookEvents(): void { @@ -96,6 +101,7 @@ export class ModesHoverController implements IEditorContribution { this._toUnhook.add(this._editor.onDidChangeModelDecorations(() => this._onModelDecorationsChanged())); } else { this._toUnhook.add(this._editor.onMouseMove(hideWidgetsEventHandler)); + this._toUnhook.add(this._editor.onKeyDown((e: IKeyboardEvent) => this._onKeyDown(e))); } this._toUnhook.add(this._editor.onMouseLeave(hideWidgetsEventHandler)); @@ -205,7 +211,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._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); } @@ -312,29 +318,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 46195dbeb5bcc..b1c73fad4a37b 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 9077e4a9eb0cf..06278716db2ed 100644 --- a/src/vs/editor/contrib/hover/hoverWidgets.ts +++ b/src/vs/editor/contrib/hover/hoverWidgets.ts @@ -3,27 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toggleClass } from 'vs/base/browser/dom'; +import * as dom 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; - private 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 +35,24 @@ export class ContentHoverWidget extends Widget implements IContentWidget { protected set isVisible(value: boolean) { this._isVisible = value; - toggleClass(this._containerDomNode, 'hidden', !this._isVisible); + dom.toggleClass(this._hover.containerDomNode, '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 +78,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 +94,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 +103,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 +137,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 +182,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 +203,7 @@ export class GlyphHoverWidget extends Widget implements IOverlayWidget { protected set isVisible(value: boolean) { this._isVisible = value; - toggleClass(this._domNode, 'hidden', !this._isVisible); + dom.toggleClass(this._domNode, '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 485175ffa7a12..46cb4d7a542c9 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -13,7 +13,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { DocumentColorProvider, Hover as MarkdownHover, HoverProviderRegistry, IColor } from 'vs/editor/common/modes'; +import { DocumentColorProvider, Hover as MarkdownHover, HoverProviderRegistry, IColor, TokenizationRegistry, CodeActionTriggerType } from 'vs/editor/common/modes'; import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color'; import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector'; import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel'; @@ -22,7 +22,7 @@ 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 { IThemeService } from 'vs/platform/theme/common/themeService'; +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'; import { basename } from 'vs/base/common/resources'; @@ -34,11 +34,14 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { getCodeActions, CodeActionSet } from 'vs/editor/contrib/codeAction/codeAction'; import { QuickFixAction, QuickFixController } from 'vs/editor/contrib/codeAction/codeActionCommands'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; +import { CodeActionKind, CodeActionTrigger } from 'vs/editor/contrib/codeAction/types'; 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 { 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.$; @@ -186,6 +189,11 @@ class ModesContentComputer implements IHoverComputer { } } +const markerCodeActionTrigger: CodeActionTrigger = { + type: CodeActionTriggerType.Manual, + filter: { include: CodeActionKind.QuickFix } +}; + export class ModesContentHoverWidget extends ContentHoverWidget { static readonly ID = 'editor.contrib.modesContentHoverWidget'; @@ -199,17 +207,20 @@ export class ModesContentHoverWidget extends ContentHoverWidget { private _shouldFocus: boolean; private _colorPicker: ColorPickerWidget | null; + 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 | null = NullOpenerService, + private readonly _openerService: IOpenerService = NullOpenerService, ) { - super(ModesContentHoverWidget.ID, editor); + super(ModesContentHoverWidget.ID, editor, _hoverVisibleKey, keybindingService); this._messages = []; this._lastRange = null; @@ -229,15 +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._hover.contentsDomNode.textContent = ''; + this._renderMessages(this._lastRange, this._messages); + } + })); } dispose(): void { @@ -446,7 +463,7 @@ export class ModesContentHoverWidget extends ContentHoverWidget { const renderer = markdownDisposeables.add(new MarkdownRenderer(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); @@ -489,10 +506,34 @@ export class ModesContentHoverWidget extends ContentHoverWidget { messageElement.innerText = message; if (source || code) { - const detailsElement = dom.append(markerElement, $('span')); - detailsElement.style.opacity = '0.6'; - detailsElement.style.paddingLeft = '6px'; - detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`; + // 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})`; + } } if (isNonEmptyArray(relatedInformation)) { @@ -523,12 +564,12 @@ 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(); } })); @@ -541,14 +582,13 @@ export class ModesContentHoverWidget extends ContentHoverWidget { 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'; - if (!actions.actions.length) { + if (!actions.validActions.length) { actions.dispose(); quickfixPlaceholderElement.textContent = nls.localize('noQuickFixes', "No quick fixes available"); return; @@ -562,14 +602,17 @@ export class ModesContentHoverWidget extends ContentHoverWidget { } })); - disposables.add(this.renderAction(actionsElement, { + 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(actions, { + // 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 }); @@ -586,30 +629,12 @@ export class ModesContentHoverWidget extends ContentHoverWidget { return getCodeActions( this._editor.getModel()!, new Range(marker.startLineNumber, marker.startColumn, marker.endLineNumber, marker.endColumn), - { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }, + 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' }); @@ -637,3 +662,10 @@ function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean { } return true; } + +registerThemingParticipant((theme, collector) => { + const linkFg = theme.getColor(textLinkForeground); + if (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 85dece77b64b2..32c6cf146563c 100644 --- a/src/vs/editor/contrib/hover/modesGlyphHover.ts +++ b/src/vs/editor/contrib/hover/modesGlyphHover.ts @@ -97,7 +97,7 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { constructor( editor: ICodeEditor, modeService: IModeService, - openerService: IOpenerService | null = NullOpenerService, + openerService: IOpenerService = NullOpenerService, ) { super(ModesGlyphHoverWidget.ID, editor); diff --git a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts index a64be8e0109de..0d069f4f6c372 100644 --- a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts +++ b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -export class InPlaceReplaceCommand implements editorCommon.ICommand { +export class InPlaceReplaceCommand implements ICommand { private readonly _editRange: Range; private readonly _originalSelection: Selection; @@ -20,11 +20,11 @@ export class InPlaceReplaceCommand implements editorCommon.ICommand { this._text = text; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { builder.addTrackedEditOperation(this._editRange, this._text); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { const inverseEditOperations = helper.getInverseEditOperations(); const srcRange = inverseEditOperations[0].range; diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index bbddafa5346c4..11c8b67f99a66 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -22,7 +22,7 @@ import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; import { IModelService } from 'vs/editor/common/services/modelService'; import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; export function getReindentEditOperations(model: ITextModel, startLineNumber: number, endLineNumber: number, inheritedIndent?: string): IIdentifiedSingleEditOperation[] { if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) { @@ -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 */); } } @@ -442,7 +442,7 @@ export class AutoIndentOnPaste implements IEditorContribution { this.callOnModel.clear(); // we are disabled - if (!this.editor.getOption(EditorOption.autoIndent) || this.editor.getOption(EditorOption.formatOnPaste)) { + if (this.editor.getOption(EditorOption.autoIndent) < EditorAutoIndentStrategy.Full || this.editor.getOption(EditorOption.formatOnPaste)) { return; } @@ -451,7 +451,7 @@ export class AutoIndentOnPaste implements IEditorContribution { return; } - this.callOnModel.add(this.editor.onDidPaste((range: Range) => { + this.callOnModel.add(this.editor.onDidPaste(({ range }) => { this.trigger(range); })); } @@ -470,6 +470,7 @@ export class AutoIndentOnPaste implements IEditorContribution { if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { return; } + const autoIndent = this.editor.getOption(EditorOption.autoIndent); const { tabSize, indentSize, insertSpaces } = model.getOptions(); this.editor.pushUndoStop(); let textEdits: TextEdit[] = []; @@ -499,7 +500,7 @@ export class AutoIndentOnPaste implements IEditorContribution { let firstLineText = model.getLineContent(startLineNumber); if (!/\S/.test(firstLineText.substring(0, range.startColumn - 1))) { - let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(model, model.getLanguageIdentifier().id, startLineNumber, indentConverter); + let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(autoIndent, model, model.getLanguageIdentifier().id, startLineNumber, indentConverter); if (indentOfFirstLine !== null) { let oldIndentation = strings.getLeadingWhitespace(firstLineText); @@ -557,7 +558,7 @@ export class AutoIndentOnPaste implements IEditorContribution { } } }; - let indentOfSecondLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdentifier().id, startLineNumber + 1, indentConverter); + let indentOfSecondLine = LanguageConfigurationRegistry.getGoodIndentForLine(autoIndent, virtualModel, model.getLanguageIdentifier().id, startLineNumber + 1, indentConverter); if (indentOfSecondLine !== null) { let newSpaceCntOfSecondLine = indentUtils.getSpaceCnt(indentOfSecondLine, tabSize); let oldSpaceCntOfSecondLine = indentUtils.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(startLineNumber + 1)), tabSize); diff --git a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts index 3d20782006186..6b76a75b7632f 100644 --- a/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/copyLinesCommand.ts @@ -5,10 +5,10 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -export class CopyLinesCommand implements editorCommon.ICommand { +export class CopyLinesCommand implements ICommand { private readonly _selection: Selection; private readonly _isCopyingDown: boolean; @@ -27,7 +27,7 @@ export class CopyLinesCommand implements editorCommon.ICommand { this._endLineNumberDelta = 0; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let s = this._selection; this._startLineNumberDelta = 0; @@ -61,7 +61,7 @@ export class CopyLinesCommand implements editorCommon.ICommand { this._selectionDirection = this._selection.getDirection(); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { let result = helper.getTrackedSelection(this._selectionId!); if (this._startLineNumberDelta !== 0 || this._endLineNumberDelta !== 0) { diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index b615da8257b3a..bc7fd660d8407 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 commands: ICommand[] = []; - const selections = editor.getSelections() || []; + 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[] = []; 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(); @@ -64,7 +88,7 @@ class CopyLinesUpAction extends AbstractCopyLinesAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miCopyLinesUp', comment: ['&& denotes a mnemonic'] }, "&&Copy Line Up"), @@ -87,7 +111,7 @@ class CopyLinesDownAction extends AbstractCopyLinesAction { linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miCopyLinesDown', comment: ['&& denotes a mnemonic'] }, "Co&&py Line Down"), @@ -105,7 +129,7 @@ export class DuplicateSelectionAction extends EditorAction { label: nls.localize('duplicateSelection', "Duplicate Selection"), alias: 'Duplicate Selection', precondition: EditorContextKeys.writable, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miDuplicateSelection', comment: ['&& denotes a mnemonic'] }, "&&Duplicate Selection"), @@ -178,7 +202,7 @@ class MoveLinesUpAction extends AbstractMoveLinesAction { linux: { primary: KeyMod.Alt | KeyCode.UpArrow }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miMoveLinesUp', comment: ['&& denotes a mnemonic'] }, "Mo&&ve Line Up"), @@ -201,7 +225,7 @@ class MoveLinesDownAction extends AbstractMoveLinesAction { linux: { primary: KeyMod.Alt | KeyCode.DownArrow }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '2_line', title: nls.localize({ key: 'miMoveLinesDown', comment: ['&& denotes a mnemonic'] }, "Move &&Line Down"), @@ -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/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts index f1b0fd7befb50..d9600938cc678 100644 --- a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts @@ -13,18 +13,19 @@ import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { IIndentConverter, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils'; +import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; export class MoveLinesCommand implements ICommand { private readonly _selection: Selection; private readonly _isMovingDown: boolean; - private readonly _autoIndent: boolean; + private readonly _autoIndent: EditorAutoIndentStrategy; private _selectionId: string | null; private _moveEndPositionDown?: boolean; private _moveEndLineSelectionShrink: boolean; - constructor(selection: Selection, isMovingDown: boolean, autoIndent: boolean) { + constructor(selection: Selection, isMovingDown: boolean, autoIndent: EditorAutoIndentStrategy) { this._selection = selection; this._isMovingDown = isMovingDown; this._autoIndent = autoIndent; @@ -117,7 +118,7 @@ export class MoveLinesCommand implements ICommand { return model.getLineContent(lineNumber); } }; - let indentOfMovingLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition( + let indentOfMovingLine = LanguageConfigurationRegistry.getGoodIndentForLine(this._autoIndent, virtualModel, model.getLanguageIdAtPosition( movingLineNumber, 1), s.startLineNumber, indentConverter); if (indentOfMovingLine !== null) { let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber)); @@ -152,7 +153,7 @@ export class MoveLinesCommand implements ICommand { } }; - let newIndentatOfMovingBlock = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition( + let newIndentatOfMovingBlock = LanguageConfigurationRegistry.getGoodIndentForLine(this._autoIndent, virtualModel, model.getLanguageIdAtPosition( movingLineNumber, 1), s.startLineNumber + 1, indentConverter); if (newIndentatOfMovingBlock !== null) { @@ -197,7 +198,7 @@ export class MoveLinesCommand implements ICommand { } } else { // it doesn't match any onEnter rule, let's check indentation rules then. - let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(s.startLineNumber, 1), movingLineNumber, indentConverter); + let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(this._autoIndent, virtualModel, model.getLanguageIdAtPosition(s.startLineNumber, 1), movingLineNumber, indentConverter); if (indentOfFirstLine !== null) { // adjust the indentation of the moving block let oldIndent = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber)); @@ -251,20 +252,19 @@ export class MoveLinesCommand implements ICommand { } let maxColumn = model.getLineMaxColumn(validPrecedingLine); - let enter = LanguageConfigurationRegistry.getEnterAction(model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); if (enter) { let enterPrefix = enter.indentation; - let enterAction = enter.enterAction; - if (enterAction.indentAction === IndentAction.None) { - enterPrefix = enter.indentation + enterAction.appendText; - } else if (enterAction.indentAction === IndentAction.Indent) { - enterPrefix = enter.indentation + enterAction.appendText; - } else if (enterAction.indentAction === IndentAction.IndentOutdent) { + if (enter.indentAction === IndentAction.None) { + enterPrefix = enter.indentation + enter.appendText; + } else if (enter.indentAction === IndentAction.Indent) { + enterPrefix = enter.indentation + enter.appendText; + } else if (enter.indentAction === IndentAction.IndentOutdent) { enterPrefix = enter.indentation; - } else if (enterAction.indentAction === IndentAction.Outdent) { - enterPrefix = indentConverter.unshiftIndent(enter.indentation) + enterAction.appendText; + } else if (enter.indentAction === IndentAction.Outdent) { + enterPrefix = indentConverter.unshiftIndent(enter.indentation) + enter.appendText; } let movingLineText = model.getLineContent(line); if (this.trimLeft(movingLineText).indexOf(this.trimLeft(enterPrefix)) >= 0) { @@ -288,7 +288,7 @@ export class MoveLinesCommand implements ICommand { } private shouldAutoIndent(model: ITextModel, selection: Selection) { - if (!this._autoIndent) { + if (this._autoIndent < EditorAutoIndentStrategy.Full) { return false; } // if it's not easy to tokenize, we stop auto indent. diff --git a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts index a201370cea3ba..0a1bc6bf90c18 100644 --- a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts @@ -6,10 +6,10 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; -export class SortLinesCommand implements editorCommon.ICommand { +export class SortLinesCommand implements ICommand { private static _COLLATOR: Intl.Collator | null = null; public static getCollator(): Intl.Collator { @@ -29,7 +29,7 @@ export class SortLinesCommand implements editorCommon.ICommand { this.selectionId = null; } - public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void { + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { let op = sortLines(model, this.selection, this.descending); if (op) { builder.addEditOperation(op.range, op.text); @@ -38,7 +38,7 @@ export class SortLinesCommand implements editorCommon.ICommand { this.selectionId = builder.trackSelection(this.selection); } - public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection { + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { return helper.getTrackedSelection(this.selectionId!); } diff --git a/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/copyLinesCommand.test.ts index 446c947cf8dc7..6777693d4ea92 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 586632150888c..cdcb7f491280a 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'; @@ -12,6 +11,20 @@ import { ITextModel } from 'vs/editor/common/model'; import { TitleCaseAction, DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction, DeleteLinesAction } from 'vs/editor/contrib/linesOperations/linesOperations'; 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)) { + expected = [expected]; + } + assert.deepEqual(editor.getSelections(), expected); +} + +function executeAction(action: EditorAction, editor: ICodeEditor): void { + action.run(null!, editor, undefined); +} suite('Editor Contrib - Line Operations', () => { suite('SortLinesAscendingAction', () => { @@ -26,13 +39,13 @@ suite('Editor Contrib - Line Operations', () => { let sortLinesAscendingAction = new SortLinesAscendingAction(); editor.setSelection(new Selection(1, 1, 3, 5)); - sortLinesAscendingAction.run(null!, editor); + executeAction(sortLinesAscendingAction, editor); assert.deepEqual(model.getLinesContent(), [ 'alpha', 'beta', 'omicron' ]); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 3, 7).toString()); + assertSelection(editor, new Selection(1, 1, 3, 7)); }); }); @@ -51,7 +64,7 @@ suite('Editor Contrib - Line Operations', () => { let sortLinesAscendingAction = new SortLinesAscendingAction(); editor.setSelections([new Selection(1, 1, 3, 5), new Selection(5, 1, 7, 5)]); - sortLinesAscendingAction.run(null!, editor); + executeAction(sortLinesAscendingAction, editor); assert.deepEqual(model.getLinesContent(), [ 'alpha', 'beta', @@ -84,13 +97,13 @@ suite('Editor Contrib - Line Operations', () => { let sortLinesDescendingAction = new SortLinesDescendingAction(); editor.setSelection(new Selection(1, 1, 3, 7)); - sortLinesDescendingAction.run(null!, editor); + executeAction(sortLinesDescendingAction, editor); assert.deepEqual(model.getLinesContent(), [ 'omicron', 'beta', 'alpha' ]); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 3, 5).toString()); + assertSelection(editor, new Selection(1, 1, 3, 5)); }); }); @@ -109,7 +122,7 @@ suite('Editor Contrib - Line Operations', () => { let sortLinesDescendingAction = new SortLinesDescendingAction(); editor.setSelections([new Selection(1, 1, 3, 7), new Selection(5, 1, 7, 7)]); - sortLinesDescendingAction.run(null!, editor); + executeAction(sortLinesDescendingAction, editor); assert.deepEqual(model.getLinesContent(), [ 'omicron', 'beta', @@ -143,13 +156,13 @@ suite('Editor Contrib - Line Operations', () => { let deleteAllLeftAction = new DeleteAllLeftAction(); editor.setSelection(new Selection(1, 2, 1, 2)); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'ne', '001'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(1), 'ne'); editor.setSelections([new Selection(2, 2, 2, 2), new Selection(3, 2, 3, 2)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'wo', '002'); - assert.equal(model.getLineContent(3), 'hree', '003'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(2), 'wo'); + assert.equal(model.getLineContent(3), 'hree'); }); }); @@ -164,16 +177,16 @@ suite('Editor Contrib - Line Operations', () => { let deleteAllLeftAction = new DeleteAllLeftAction(); editor.setSelection(new Selection(2, 1, 2, 1)); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'onetwo', '001'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(1), 'onetwo'); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); assert.equal(model.getLinesContent()[0], 'onetwothree'); assert.equal(model.getLinesContent().length, 1); editor.setSelection(new Selection(1, 1, 1, 1)); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); assert.equal(model.getLinesContent()[0], 'onetwothree'); }); }); @@ -197,7 +210,7 @@ suite('Editor Contrib - Line Operations', () => { editor.setSelections([beforeSecondWasoSelection, endOfBCCSelection, endOfNonono]); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); let selections = editor.getSelections()!; assert.equal(model.getLineContent(2), ''); @@ -225,7 +238,7 @@ suite('Editor Contrib - Line Operations', () => { selections[2].endColumn ], [5, 1, 5, 1]); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); selections = editor.getSelections()!; assert.equal(model.getLineContent(1), 'hi my name is Carlos Matos waso waso'); @@ -263,24 +276,24 @@ suite('Editor Contrib - Line Operations', () => { let deleteAllLeftAction = new DeleteAllLeftAction(); editor.setSelections([new Selection(1, 2, 1, 2), new Selection(1, 4, 1, 4)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'lo', '001'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(1), 'lo'); editor.setSelections([new Selection(2, 2, 2, 2), new Selection(2, 4, 2, 5)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'd', '002'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(2), 'd'); editor.setSelections([new Selection(3, 2, 3, 5), new Selection(3, 7, 3, 7)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(3), 'world', '003'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(3), 'world'); editor.setSelections([new Selection(4, 3, 4, 3), new Selection(4, 5, 5, 4)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(4), 'jour', '004'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(4), 'jour'); editor.setSelections([new Selection(5, 3, 6, 3), new Selection(6, 5, 7, 5), new Selection(7, 7, 7, 7)]); - deleteAllLeftAction.run(null!, editor); - assert.equal(model.getLineContent(5), 'world', '005'); + executeAction(deleteAllLeftAction, editor); + assert.equal(model.getLineContent(5), 'world'); }); }); @@ -300,11 +313,11 @@ suite('Editor Contrib - Line Operations', () => { assert.equal(model.getLineContent(1), 'Typing some text here on line one'); assert.deepEqual(editor.getSelection(), new Selection(1, 31, 1, 31)); - deleteAllLeftAction.run(null!, editor); + executeAction(deleteAllLeftAction, editor); 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)); }); @@ -331,29 +344,29 @@ suite('Editor Contrib - Line Operations', () => { let joinLinesAction = new JoinLinesAction(); editor.setSelection(new Selection(1, 2, 1, 2)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello world', '001'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 6, 1, 6).toString(), '002'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(1), 'hello world'); + assertSelection(editor, new Selection(1, 6, 1, 6)); editor.setSelection(new Selection(2, 2, 2, 2)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'hello world', '003'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 7, 2, 7).toString(), '004'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(2), 'hello world'); + assertSelection(editor, new Selection(2, 7, 2, 7)); editor.setSelection(new Selection(3, 2, 3, 2)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(3), 'hello world', '005'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(3, 7, 3, 7).toString(), '006'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(3), 'hello world'); + assertSelection(editor, new Selection(3, 7, 3, 7)); editor.setSelection(new Selection(4, 2, 5, 3)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(4), 'hello world', '007'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(4, 2, 4, 8).toString(), '008'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(4), 'hello world'); + assertSelection(editor, new Selection(4, 2, 4, 8)); editor.setSelection(new Selection(5, 1, 7, 3)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(5), 'hello world', '009'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(5, 1, 5, 3).toString(), '010'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(5), 'hello world'); + assertSelection(editor, new Selection(5, 1, 5, 3)); }); }); @@ -367,10 +380,10 @@ suite('Editor Contrib - Line Operations', () => { let joinLinesAction = new JoinLinesAction(); editor.setSelection(new Selection(2, 1, 2, 1)); - joinLinesAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello', '001'); - assert.equal(model.getLineContent(2), 'world', '002'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 6, 2, 6).toString(), '003'); + executeAction(joinLinesAction, editor); + assert.equal(model.getLineContent(1), 'hello'); + assert.equal(model.getLineContent(2), 'world'); + assertSelection(editor, new Selection(2, 6, 2, 6)); }); }); @@ -402,19 +415,16 @@ suite('Editor Contrib - Line Operations', () => { new Selection(10, 1, 10, 1) ]); - joinLinesAction.run(null!, editor); - assert.equal(model.getLinesContent().join('\n'), 'hello world\nhello world\nhello world\nhello world\n\nhello world', '001'); - assert.deepEqual(editor.getSelections()!.toString(), [ + executeAction(joinLinesAction, editor); + assert.equal(model.getLinesContent().join('\n'), 'hello world\nhello world\nhello world\nhello world\n\nhello world'); + assertSelection(editor, [ /** primary cursor */ new Selection(3, 4, 3, 8), new Selection(1, 6, 1, 6), new Selection(2, 2, 2, 8), new Selection(4, 5, 4, 9), new Selection(6, 1, 6, 1) - ].toString(), '002'); - - /** primary cursor */ - assert.deepEqual(editor.getSelection()!.toString(), new Selection(3, 4, 3, 8).toString(), '003'); + ]); }); }); @@ -433,11 +443,11 @@ suite('Editor Contrib - Line Operations', () => { assert.equal(model.getLineContent(1), 'hello my dear'); assert.deepEqual(editor.getSelection(), new Selection(1, 14, 1, 14)); - joinLinesAction.run(null!, editor); + executeAction(joinLinesAction, editor); 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)); }); @@ -456,29 +466,29 @@ suite('Editor Contrib - Line Operations', () => { let transposeAction = new TransposeAction(); editor.setSelection(new Selection(1, 1, 1, 1)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello world', '001'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 2, 1, 2).toString(), '002'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(1), 'hello world'); + assertSelection(editor, new Selection(1, 2, 1, 2)); editor.setSelection(new Selection(1, 6, 1, 6)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hell oworld', '003'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 7, 1, 7).toString(), '004'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(1), 'hell oworld'); + assertSelection(editor, new Selection(1, 7, 1, 7)); editor.setSelection(new Selection(1, 12, 1, 12)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hell oworl', '005'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 2, 2, 2).toString(), '006'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(1), 'hell oworl'); + assertSelection(editor, new Selection(2, 2, 2, 2)); editor.setSelection(new Selection(3, 1, 3, 1)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(3), '', '007'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(4, 1, 4, 1).toString(), '008'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(3), ''); + assertSelection(editor, new Selection(4, 1, 4, 1)); editor.setSelection(new Selection(4, 2, 4, 2)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(4), ' ', '009'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(4, 3, 4, 3).toString(), '010'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(4), ' '); + assertSelection(editor, new Selection(4, 3, 4, 3)); } ); @@ -498,24 +508,24 @@ suite('Editor Contrib - Line Operations', () => { let transposeAction = new TransposeAction(); editor.setSelection(new Selection(1, 1, 1, 1)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(2), '', '011'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 1).toString(), '012'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(2), ''); + assertSelection(editor, new Selection(2, 1, 2, 1)); editor.setSelection(new Selection(3, 6, 3, 6)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(4), 'oworld', '013'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(4, 2, 4, 2).toString(), '014'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(4), 'oworld'); + assertSelection(editor, new Selection(4, 2, 4, 2)); editor.setSelection(new Selection(6, 12, 6, 12)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(7), 'd', '015'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(7, 2, 7, 2).toString(), '016'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(7), 'd'); + assertSelection(editor, new Selection(7, 2, 7, 2)); editor.setSelection(new Selection(8, 12, 8, 12)); - transposeAction.run(null!, editor); - assert.equal(model.getLineContent(8), 'hello world', '019'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(8, 12, 8, 12).toString(), '020'); + executeAction(transposeAction, editor); + assert.equal(model.getLineContent(8), 'hello world'); + assertSelection(editor, new Selection(8, 12, 8, 12)); } ); }); @@ -532,44 +542,44 @@ suite('Editor Contrib - Line Operations', () => { let titlecaseAction = new TitleCaseAction(); editor.setSelection(new Selection(1, 1, 1, 12)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'HELLO WORLD', '001'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 12).toString(), '002'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(1), 'HELLO WORLD'); + assertSelection(editor, new Selection(1, 1, 1, 12)); editor.setSelection(new Selection(1, 1, 1, 12)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello world', '003'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 12).toString(), '004'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(1), 'hello world'); + assertSelection(editor, new Selection(1, 1, 1, 12)); editor.setSelection(new Selection(1, 3, 1, 3)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'HELLO world', '005'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 3, 1, 3).toString(), '006'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(1), 'HELLO world'); + assertSelection(editor, new Selection(1, 3, 1, 3)); editor.setSelection(new Selection(1, 4, 1, 4)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'hello world', '007'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 4, 1, 4).toString(), '008'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(1), 'hello world'); + assertSelection(editor, new Selection(1, 4, 1, 4)); editor.setSelection(new Selection(1, 1, 1, 12)); - titlecaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), 'Hello World', '009'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 12).toString(), '010'); + executeAction(titlecaseAction, editor); + assert.equal(model.getLineContent(1), 'Hello World'); + assertSelection(editor, new Selection(1, 1, 1, 12)); editor.setSelection(new Selection(2, 1, 2, 6)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'ÖÇŞĞÜ', '011'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '012'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(2), 'ÖÇŞĞÜ'); + assertSelection(editor, new Selection(2, 1, 2, 6)); editor.setSelection(new Selection(2, 1, 2, 6)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'öçşğü', '013'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '014'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(2), 'öçşğü'); + assertSelection(editor, new Selection(2, 1, 2, 6)); editor.setSelection(new Selection(2, 1, 2, 6)); - titlecaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), 'Öçşğü', '015'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 1, 2, 6).toString(), '016'); + executeAction(titlecaseAction, editor); + assert.equal(model.getLineContent(2), 'Öçşğü'); + assertSelection(editor, new Selection(2, 1, 2, 6)); } ); @@ -586,27 +596,27 @@ suite('Editor Contrib - Line Operations', () => { let titlecaseAction = new TitleCaseAction(); editor.setSelection(new Selection(1, 1, 1, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(1), 'Foo Bar Baz'); editor.setSelection(new Selection(2, 1, 2, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(2), 'Foo\'Bar\'Baz'); editor.setSelection(new Selection(3, 1, 3, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(3), 'Foo[Bar]Baz'); editor.setSelection(new Selection(4, 1, 4, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(4), 'Foo`Bar~Baz'); editor.setSelection(new Selection(5, 1, 5, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(5), 'Foo^Bar%Baz'); editor.setSelection(new Selection(6, 1, 6, 12)); - titlecaseAction.run(null!, editor); + executeAction(titlecaseAction, editor); assert.equal(model.getLineContent(6), 'Foo$Bar!Baz'); } ); @@ -621,24 +631,24 @@ suite('Editor Contrib - Line Operations', () => { let lowercaseAction = new LowerCaseAction(); editor.setSelection(new Selection(1, 1, 1, 1)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), '', '013'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 1).toString(), '014'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(1), ''); + assertSelection(editor, new Selection(1, 1, 1, 1)); editor.setSelection(new Selection(1, 1, 1, 1)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(1), '', '015'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(1, 1, 1, 1).toString(), '016'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(1), ''); + assertSelection(editor, new Selection(1, 1, 1, 1)); editor.setSelection(new Selection(2, 2, 2, 2)); - uppercaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), ' ', '017'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 2, 2, 2).toString(), '018'); + executeAction(uppercaseAction, editor); + assert.equal(model.getLineContent(2), ' '); + assertSelection(editor, new Selection(2, 2, 2, 2)); editor.setSelection(new Selection(2, 2, 2, 2)); - lowercaseAction.run(null!, editor); - assert.equal(model.getLineContent(2), ' ', '019'); - assert.deepEqual(editor.getSelection()!.toString(), new Selection(2, 2, 2, 2).toString(), '020'); + executeAction(lowercaseAction, editor); + assert.equal(model.getLineContent(2), ' '); + assertSelection(editor, new Selection(2, 2, 2, 2)); } ); }); @@ -649,17 +659,17 @@ suite('Editor Contrib - Line Operations', () => { const model = editor.getModel()!; const action = new DeleteAllRightAction(); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); editor.setSelection(new Selection(1, 1, 1, 1)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(1, 1, 1, 1), new Selection(1, 1, 1, 1)]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); }); @@ -674,17 +684,17 @@ suite('Editor Contrib - Line Operations', () => { const action = new DeleteAllRightAction(); editor.setSelection(new Selection(1, 2, 1, 5)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['ho', 'world']); assert.deepEqual(editor.getSelections(), [new Selection(1, 2, 1, 2)]); editor.setSelection(new Selection(1, 1, 2, 4)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['ld']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); editor.setSelection(new Selection(1, 1, 1, 3)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['']); assert.deepEqual(editor.getSelections(), [new Selection(1, 1, 1, 1)]); }); @@ -699,12 +709,12 @@ suite('Editor Contrib - Line Operations', () => { const action = new DeleteAllRightAction(); editor.setSelection(new Selection(1, 3, 1, 3)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he', 'world']); assert.deepEqual(editor.getSelections(), [new Selection(1, 3, 1, 3)]); editor.setSelection(new Selection(2, 1, 2, 1)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he', '']); assert.deepEqual(editor.getSelections(), [new Selection(2, 1, 2, 1)]); }); @@ -719,17 +729,17 @@ suite('Editor Contrib - Line Operations', () => { const action = new DeleteAllRightAction(); editor.setSelection(new Selection(1, 6, 1, 6)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['helloworld']); assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); editor.setSelection(new Selection(1, 6, 1, 6)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hello']); assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); editor.setSelection(new Selection(1, 6, 1, 6)); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hello']); assert.deepEqual(editor.getSelections(), [new Selection(1, 6, 1, 6)]); }); @@ -749,34 +759,34 @@ suite('Editor Contrib - Line Operations', () => { new Selection(1, 6, 1, 6), new Selection(3, 4, 3, 4), ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hethere', 'wor']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), new Selection(2, 4, 2, 4) ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he', 'wor']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), new Selection(2, 4, 2, 4) ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hewor']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), new Selection(1, 6, 1, 6) ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3) ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['he']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3) @@ -798,20 +808,20 @@ suite('Editor Contrib - Line Operations', () => { new Selection(1, 6, 1, 6), new Selection(3, 4, 3, 4), ]); - action.run(null!, editor); + executeAction(action, editor); assert.deepEqual(model.getLinesContent(), ['hethere', 'wor']); assert.deepEqual(editor.getSelections(), [ new Selection(1, 3, 1, 3), 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) @@ -821,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(); - insertLineBeforeAction.run(null!, editor); - callback(editor.getModel()!, cursor); + executeAction(insertLineBeforeAction, editor); + 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), ''); @@ -862,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(); - insertLineAfterAction.run(null!, editor); - callback(editor.getModel()!, cursor); + executeAction(insertLineAfterAction, editor); + 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'); @@ -917,7 +927,7 @@ suite('Editor Contrib - Line Operations', () => { let indentLinesAction = new IndentLinesAction(); editor.setPosition(new Position(1, 2)); - indentLinesAction.run(null!, editor); + executeAction(indentLinesAction, editor); assert.equal(model.getLineContent(1), '\tfunction baz() {'); assert.deepEqual(editor.getSelection(), new Selection(1, 3, 1, 3)); @@ -942,7 +952,7 @@ suite('Editor Contrib - Line Operations', () => { const indentLinesAction = new IndentLinesAction(); editor.setPosition(new Position(1, 1)); - indentLinesAction.run(null!, editor); + executeAction(indentLinesAction, editor); assert.equal(model.getLineContent(1), '\tSome text'); assert.deepEqual(editor.getSelection(), new Selection(1, 2, 1, 2)); }); @@ -950,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', @@ -964,7 +993,7 @@ suite('Editor Contrib - Line Operations', () => { new Selection(3, 4, 3, 4), ]); const deleteLinesAction = new DeleteLinesAction(); - deleteLinesAction.run(null!, editor); + executeAction(deleteLinesAction, editor); assert.equal(editor.getValue(), 'a\nc'); }); @@ -976,7 +1005,7 @@ suite('Editor Contrib - Line Operations', () => { withTestCodeEditor(initialText, {}, (editor) => { editor.setSelections(initialSelections); const deleteLinesAction = new DeleteLinesAction(); - deleteLinesAction.run(null!, editor); + executeAction(deleteLinesAction, editor); assert.equal(editor.getValue(), resultingText.join('\n')); assert.deepEqual(editor.getSelections(), resultingSelections); diff --git a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts index 1c7da6e5d6649..b803557c63eec 100644 --- a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts @@ -9,21 +9,22 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/moveLinesCommand'; import { testCommand } from 'vs/editor/test/browser/testCommand'; import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; function testMoveLinesDownCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { - testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, true, false), expectedLines, expectedSelection); + testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, true, EditorAutoIndentStrategy.Advanced), expectedLines, expectedSelection); } function testMoveLinesUpCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { - testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, false, false), expectedLines, expectedSelection); + testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, false, EditorAutoIndentStrategy.Advanced), expectedLines, expectedSelection); } function testMoveLinesDownWithIndentCommand(languageId: LanguageIdentifier, lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { - testCommand(lines, languageId, selection, (sel) => new MoveLinesCommand(sel, true, true), expectedLines, expectedSelection); + testCommand(lines, languageId, selection, (sel) => new MoveLinesCommand(sel, true, EditorAutoIndentStrategy.Full), expectedLines, expectedSelection); } function testMoveLinesUpWithIndentCommand(languageId: LanguageIdentifier, lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { - testCommand(lines, languageId, selection, (sel) => new MoveLinesCommand(sel, false, true), expectedLines, expectedSelection); + testCommand(lines, languageId, selection, (sel) => new MoveLinesCommand(sel, false, EditorAutoIndentStrategy.Full), expectedLines, expectedSelection); } suite('Editor Contrib - Move Lines Command', () => { diff --git a/src/vs/editor/contrib/links/getLinks.ts b/src/vs/editor/contrib/links/getLinks.ts index df34eed560087..be5c7416457bb 100644 --- a/src/vs/editor/contrib/links/getLinks.ts +++ b/src/vs/editor/contrib/links/getLinks.ts @@ -13,6 +13,7 @@ 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 { coalesce } from 'vs/base/common/arrays'; +import { assertType } from 'vs/base/common/types'; export class Link implements ILink { @@ -44,17 +45,9 @@ export class Link implements ILink { return this._link.tooltip; } - resolve(token: CancellationToken): Promise { + async resolve(token: CancellationToken): Promise { if (this._link.url) { - try { - if (typeof this._link.url === 'string') { - return Promise.resolve(URI.parse(this._link.url)); - } else { - return Promise.resolve(this._link.url); - } - } catch (e) { - return Promise.reject(new Error('invalid')); - } + return this._link.url; } if (typeof this._provider.resolveLink === 'function') { @@ -85,8 +78,8 @@ export class LinksList extends Disposable { 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._register(list); } } this.links = links; @@ -160,10 +153,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 []; @@ -172,6 +168,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 6bce7e78d8d45..9e3c455e31aa4 100644 --- a/src/vs/editor/contrib/links/links.ts +++ b/src/vs/editor/contrib/links/links.ts @@ -14,7 +14,7 @@ import * as platform from 'vs/base/common/platform'; import { ICodeEditor, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { LinkProviderRegistry } from 'vs/editor/common/modes'; @@ -25,6 +25,10 @@ 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'; +import * as strings from 'vs/base/common/strings'; function getHoverMessage(link: Link, useMetaKey: boolean): MarkdownString { const executeCmd = link.url && /^command:/i.test(link.url.toString()); @@ -97,7 +101,7 @@ class LinkOccurrence { } } -class LinkDetector implements editorCommon.IEditorContribution { +export class LinkDetector implements IEditorContribution { public static readonly ID: string = 'editor.linkDetector'; @@ -291,7 +295,29 @@ class LinkDetector implements editorCommon.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 && strings.startsWith(uri, 'file:')) { + const parsedUri = URI.parse(uri); + if (parsedUri.scheme === Schemas.file) { + const fsPath = resources.originalFSPath(parsedUri); + + let relativePath: string | null = null; + if (strings.startsWith(fsPath, '/./')) { + relativePath = `.${fsPath.substr(1)}`; + } else if (strings.startsWith(fsPath, '//./')) { + relativePath = `.${fsPath.substr(2)}`; + } + + if (relativePath) { + uri = resources.joinPath(modelUri, relativePath); + } + } + } + } + return this.openerService.open(uri, { openToSide, fromUserGesture }); }, err => { diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts index eb17228cfa8ba..b4bfe8ddd9e49 100644 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdown/markdownRenderer.ts @@ -7,7 +7,6 @@ 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 { URI } from 'vs/base/common/uri'; import { onUnexpectedError } from 'vs/base/common/errors'; import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -29,7 +28,7 @@ export class MarkdownRenderer extends Disposable { constructor( private readonly _editor: ICodeEditor, @IModeService private readonly _modeService: IModeService, - @optional(IOpenerService) private readonly _openerService: IOpenerService | null = NullOpenerService, + @optional(IOpenerService) private readonly _openerService: IOpenerService = NullOpenerService, ) { super(); } @@ -64,15 +63,7 @@ export class MarkdownRenderer extends Disposable { codeBlockRenderCallback: () => this._onDidRenderCodeBlock.fire(), actionHandler: { callback: (content) => { - let uri: URI | undefined; - try { - uri = URI.parse(content); - } catch { - // ignore - } - if (uri && this._openerService) { - this._openerService.open(uri, { fromUserGesture: true }).catch(onUnexpectedError); - } + this._openerService.open(content, { fromUserGesture: true }).catch(onUnexpectedError); }, disposeables } diff --git a/src/vs/editor/contrib/message/messageController.css b/src/vs/editor/contrib/message/messageController.css index 36c68465b2c24..a3910415dd4f5 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 e8214f9a0d923..6bec24fd00fc2 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -10,16 +10,17 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { IDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { Range } from 'vs/editor/common/core/range'; -import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { registerEditorContribution, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; 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 editorCommon.IEditorContribution { +export class MessageController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.messageController'; @@ -29,8 +30,6 @@ export class MessageController extends Disposable implements editorCommon.IEdito 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 editorCommon.IEdito 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; @@ -144,7 +144,7 @@ class MessageWidget implements IContentWidget { constructor(editor: ICodeEditor, { lineNumber, column }: IPosition, text: string) { this._editor = editor; - this._editor.revealLinesInCenterIfOutsideViewport(lineNumber, lineNumber, editorCommon.ScrollType.Smooth); + this._editor.revealLinesInCenterIfOutsideViewport(lineNumber, lineNumber, ScrollType.Smooth); this._position = { lineNumber, column: column - 1 }; this._domNode = document.createElement('div'); @@ -176,7 +176,7 @@ class MessageWidget implements IContentWidget { } getPosition(): IContentWidgetPosition { - return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE] }; + return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW] }; } } @@ -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 cadc1efd03ffe..6a7c295e8272c 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'; @@ -46,7 +45,7 @@ export class InsertCursorAbove extends EditorAction { }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miInsertCursorAbove', comment: ['&& denotes a mnemonic'] }, "&&Add Cursor Above"), @@ -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); } } @@ -95,7 +93,7 @@ export class InsertCursorBelow extends EditorAction { }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miInsertCursorBelow', comment: ['&& denotes a mnemonic'] }, "A&&dd Cursor Below"), @@ -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); } } @@ -140,7 +137,7 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_I, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miInsertCursorAtEndOfEachLineSelected', comment: ['&& denotes a mnemonic'] }, "Add C&&ursors to Line Ends"), @@ -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; } @@ -631,6 +630,12 @@ export class MultiCursorSelectionController extends Disposable implements IEdito this._setSelections(matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn))); } } + + public selectAllUsingSelections(selections: Selection[]): void { + if (selections.length > 0) { + this._setSelections(selections); + } + } } export abstract class MultiCursorSelectionControllerAction extends EditorAction { @@ -662,7 +667,7 @@ export class AddSelectionToNextFindMatchAction extends MultiCursorSelectionContr primary: KeyMod.CtrlCmd | KeyCode.KEY_D, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miAddSelectionToNextFindMatch', comment: ['&& denotes a mnemonic'] }, "Add &&Next Occurrence"), @@ -682,7 +687,7 @@ export class AddSelectionToPreviousFindMatchAction extends MultiCursorSelectionC label: nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), alias: 'Add Selection To Previous Find Match', precondition: undefined, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miAddSelectionToPreviousFindMatch', comment: ['&& denotes a mnemonic'] }, "Add P&&revious Occurrence"), @@ -740,7 +745,7 @@ export class SelectHighlightsAction extends MultiCursorSelectionControllerAction primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '3_multi', title: nls.localize({ key: 'miSelectHighlights', comment: ['&& denotes a mnemonic'] }, "Select All &&Occurrences"), @@ -765,7 +770,7 @@ export class CompatChangeAll extends MultiCursorSelectionControllerAction { primary: KeyMod.CtrlCmd | KeyCode.F2, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 1.2 } @@ -780,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; } /** @@ -801,6 +808,7 @@ class SelectionHighlighterState { a.searchText === b.searchText && a.matchCase === b.matchCase && a.wordSeparators === b.wordSeparators + && a.modelVersionId === b.modelVersionId ); } } @@ -851,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(); })); @@ -933,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 { @@ -958,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 7724699ededc9..8b25ecac5e134 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); }); }); @@ -69,7 +69,9 @@ suite('Multicursor selection', () => { store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, remove: (key) => undefined, logStorage: () => undefined, - migrate: (toWorkspace) => Promise.resolve(undefined) + migrate: (toWorkspace) => Promise.resolve(undefined), + flush: () => undefined, + isNew: () => true } as IStorageService); test('issue #8817: Cursor position changes when you cancel multicursor', () => { @@ -77,10 +79,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)); @@ -107,10 +109,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)); @@ -141,10 +143,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)); @@ -169,10 +171,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)); @@ -224,12 +226,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)); @@ -249,10 +251,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); @@ -261,7 +263,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/arrow-down-dark.svg b/src/vs/editor/contrib/parameterHints/arrow-down-dark.svg deleted file mode 100644 index b1a76d789ea95..0000000000000 --- a/src/vs/editor/contrib/parameterHints/arrow-down-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/arrow-down.svg b/src/vs/editor/contrib/parameterHints/arrow-down.svg deleted file mode 100644 index d643403d75bce..0000000000000 --- a/src/vs/editor/contrib/parameterHints/arrow-down.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/arrow-up-dark.svg b/src/vs/editor/contrib/parameterHints/arrow-up-dark.svg deleted file mode 100644 index 43f31a09521ac..0000000000000 --- a/src/vs/editor/contrib/parameterHints/arrow-up-dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/arrow-up.svg b/src/vs/editor/contrib/parameterHints/arrow-up.svg deleted file mode 100644 index b8072bb31bbbe..0000000000000 --- a/src/vs/editor/contrib/parameterHints/arrow-up.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.css b/src/vs/editor/contrib/parameterHints/parameterHints.css index dff7f77f9e463..03c4e2640ee95 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.css +++ b/src/vs/editor/contrib/parameterHints/parameterHints.css @@ -13,12 +13,12 @@ .monaco-editor .parameter-hints-widget > .wrapper { max-width: 440px; display: flex; - flex-direction: column; + flex-direction: row; } .monaco-editor .parameter-hints-widget.multiple { min-height: 3.3em; - padding: 0 0 0 1.9em; + padding: 0; } .monaco-editor .parameter-hints-widget.visible { @@ -33,7 +33,9 @@ .monaco-editor .parameter-hints-widget .monaco-scrollable-element, .monaco-editor .parameter-hints-widget .body { display: flex; + flex: 1; flex-direction: column; + min-height: 100%; } .monaco-editor .parameter-hints-widget .signature { @@ -53,6 +55,10 @@ white-space: initial; } +.monaco-editor .parameter-hints-widget .docs .markdown-docs code { + font-family: var(--monaco-monospace-font); +} + .monaco-editor .parameter-hints-widget .docs .code { white-space: pre-wrap; } @@ -62,20 +68,20 @@ padding: 0 0.4em; } -.monaco-editor .parameter-hints-widget .buttons { - position: absolute; +.monaco-editor .parameter-hints-widget .controls { display: none; - bottom: 0; - left: 0; + flex-direction: column; + align-items: center; + min-width: 22px; + justify-content: flex-end; } -.monaco-editor .parameter-hints-widget.multiple .buttons { - display: block; +.monaco-editor .parameter-hints-widget.multiple .controls { + display: flex; + padding: 0 2px; } .monaco-editor .parameter-hints-widget.multiple .button { - position: absolute; - left: 2px; width: 16px; height: 16px; background-repeat: no-repeat; @@ -84,28 +90,14 @@ .monaco-editor .parameter-hints-widget .button.previous { bottom: 24px; - background-image: url('arrow-up.svg'); -} - -.monaco-editor .parameter-hints-widget .button.next { - bottom: 0; - background-image: url('arrow-down.svg'); } .monaco-editor .parameter-hints-widget .overloads { - position: absolute; - display: none; text-align: center; - bottom: 14px; - left: 0; - width: 22px; height: 12px; line-height: 12px; opacity: 0.5; -} - -.monaco-editor .parameter-hints-widget.multiple .overloads { - display: block; + font-family: var(--monaco-monospace-font); } .monaco-editor .parameter-hints-widget .signature .parameter.active { @@ -117,15 +109,3 @@ font-weight: bold; margin-right: 0.5em; } - -/*** VS Dark & High Contrast*/ - -.monaco-editor.hc-black .parameter-hints-widget .button.previous, -.monaco-editor.vs-dark .parameter-hints-widget .button.previous { - background-image: url('arrow-up-dark.svg'); -} - -.monaco-editor.hc-black .parameter-hints-widget .button.next, -.monaco-editor.vs-dark .parameter-hints-widget .button.next { - background-image: url('arrow-down-dark.svg'); -} diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts index 7ff13c6182c80..b285d1f76658a 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsModel.ts @@ -26,7 +26,7 @@ namespace ParameterHintState { Pending, } - export const Default = new class { readonly type = Type.Default; }; + export const Default = { type: Type.Default } as const; export class Pending { readonly type = Type.Pending; diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 2ac6ede62feb9..9a3cf05bb249e 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -20,11 +20,18 @@ 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 { 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'; @@ -76,9 +83,10 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { const wrapper = dom.append(element, $('.wrapper')); wrapper.tabIndex = -1; - const buttons = dom.append(wrapper, $('.buttons')); - const previous = dom.append(buttons, $('.button.previous')); - const next = dom.append(buttons, $('.button.next')); + const controls = dom.append(wrapper, $('.controls')); + const previous = dom.append(controls, $('.button' + parameterHintsPreviousIcon.cssSelector)); + const overloads = dom.append(controls, $('.overloads')); + const next = dom.append(controls, $('.button' + parameterHintsNextIcon.cssSelector)); const onPreviousClick = stop(domEvent(previous, 'click')); this._register(onPreviousClick(this.previous, this)); @@ -86,8 +94,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { const onNextClick = stop(domEvent(next, 'click')); this._register(onNextClick(this.next, this)); - const overloads = dom.append(wrapper, $('.overloads')); - const body = $('.body'); const scrollbar = new DomScrollableElement(body, {}); this._register(scrollbar); @@ -153,6 +159,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } private hide(): void { + this.renderDisposeables.clear(); + if (!this.visible) { return; } @@ -177,6 +185,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } private render(hints: modes.SignatureHelp): void { + this.renderDisposeables.clear(); + if (!this.domNodes) { return; } @@ -185,8 +195,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { dom.toggleClass(this.domNodes.element, '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,43 +204,40 @@ 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); + const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(activeParameter.documentation)); dom.addClass(renderedContents.element, 'markdown-docs'); - this.renderDisposeables.add(renderedContents); 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); + const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(signature.documentation)); dom.addClass(renderedContents.element, 'markdown-docs'); - this.renderDisposeables.add(renderedContents); dom.append(this.domNodes.docs, renderedContents.element); } @@ -239,15 +246,11 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { dom.toggleClass(this.domNodes.signature, 'has-docs', hasDocs); dom.toggleClass(this.domNodes.docs, 'empty', !hasDocs); - let currentOverload = String(hints.activeSignature + 1); - if (hints.signatures.length < 10) { - currentOverload += `/${hints.signatures.length}`; - } - - this.domNodes.overloads.textContent = currentOverload; + this.domNodes.overloads.textContent = + pad(hints.activeSignature + 1, hints.signatures.length.toString().length) + '/' + 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. @@ -262,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); @@ -295,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; } } @@ -362,7 +365,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 2aa301d692a64..f6c75e1f194fd 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); diff --git a/src/vs/editor/contrib/peekView/media/peekViewWidget.css b/src/vs/editor/contrib/peekView/media/peekViewWidget.css index 889d1148298be..e6b5ccb09f255 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) { @@ -21,7 +22,20 @@ margin-left: 0.5em; } -.monaco-editor .peekview-widget .head .peekview-title .meta::before { +.monaco-editor .peekview-widget .head .peekview-title .meta { + 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 deaf2752ba600..f265a4bfd607c 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); @@ -260,7 +277,7 @@ export abstract class PeekViewWidget extends ZoneWidget { export const peekViewTitleBackground = registerColor('peekViewTitle.background', { dark: '#1E1E1E', light: '#FFFFFF', hc: '#0C141F' }, nls.localize('peekViewTitleBackground', 'Background color of the peek view title area.')); export const peekViewTitleForeground = registerColor('peekViewTitleLabel.foreground', { dark: '#FFFFFF', light: '#333333', hc: '#FFFFFF' }, nls.localize('peekViewTitleForeground', 'Color of the peek view title.')); -export const peekViewTitleInfoForeground = registerColor('peekViewTitleDescription.foreground', { dark: '#ccccccb3', light: '#6c6c6cb3', hc: '#FFFFFF99' }, nls.localize('peekViewTitleInfoForeground', 'Color of the peek view title info.')); +export const peekViewTitleInfoForeground = registerColor('peekViewTitleDescription.foreground', { dark: '#ccccccb3', light: '#616161e6', hc: '#FFFFFF99' }, nls.localize('peekViewTitleInfoForeground', 'Color of the peek view title info.')); export const peekViewBorder = registerColor('peekView.border', { dark: '#007acc', light: '#007acc', hc: contrastBorder }, nls.localize('peekViewBorder', 'Color of the peek view borders and arrow.')); export const peekViewResultsBackground = registerColor('peekViewResult.background', { dark: '#252526', light: '#F3F3F3', hc: Color.black }, nls.localize('peekViewResultsBackground', 'Background color of the peek view result list.')); diff --git a/src/vs/editor/contrib/quickAccess/commandsQuickAccess.ts b/src/vs/editor/contrib/quickAccess/commandsQuickAccess.ts new file mode 100644 index 0000000000000..7db794fa83ed0 --- /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 0000000000000..039dfbd30dfaf --- /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 0000000000000..0c17d7c0b410b --- /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 0000000000000..db4cc2ecbbbc5 --- /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(); + 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/quickOpen/quickOpen.ts b/src/vs/editor/contrib/quickOpen/quickOpen.ts deleted file mode 100644 index 2730114175b26..0000000000000 --- a/src/vs/editor/contrib/quickOpen/quickOpen.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 { illegalArgument } from 'vs/base/common/errors'; -import { URI } from 'vs/base/common/uri'; -import { Range } from 'vs/editor/common/core/range'; -import { ITextModel } from 'vs/editor/common/model'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import { DocumentSymbol } from 'vs/editor/common/modes'; -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'; - -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)) { - if (child instanceof OutlineElement) { - roots.push(child.symbol); - } else { - roots.push(...values(child.children).map(child => child.symbol)); - } - } - - let flatEntries: DocumentSymbol[] = []; - if (token.isCancellationRequested) { - return flatEntries; - } - if (flat) { - flatten(flatEntries, roots, ''); - } else { - flatEntries = roots; - } - - return flatEntries.sort(compareEntriesUsingStart); -} - -function compareEntriesUsingStart(a: DocumentSymbol, b: DocumentSymbol): number { - return Range.compareRangesUsingStarts(a.range, b.range); -} - -function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void { - for (let 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... - }); - if (entry.children) { - flatten(bucket, entry.children, entry.name); - } - } -} - - -registerLanguageCommand('_executeDocumentSymbolProvider', function (accessor, args) { - const { resource } = args; - if (!(resource instanceof URI)) { - throw illegalArgument('resource'); - } - const model = accessor.get(IModelService).getModel(resource); - if (model) { - return getDocumentSymbols(model, false, CancellationToken.None); - } - - return accessor.get(ITextModelService).createModelReference(resource).then(reference => { - return new Promise((resolve, reject) => { - try { - const result = getDocumentSymbols(reference.object.textEditorModel, false, CancellationToken.None); - resolve(result); - } catch (err) { - reject(err); - } - }).finally(() => { - reference.dispose(); - }); - }); -}); 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 0000000000000..16bb0178528b4 --- /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 0000000000000..105dfb2ddb2ca --- /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 8eb67d9fe0468..214d00ffc639c 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -4,17 +4,16 @@ *--------------------------------------------------------------------------------------------*/ 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 { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +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'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { RenameInputField, CONTEXT_RENAME_INPUT_VISIBLE } from './renameInputField'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { WorkspaceEdit, RenameProviderRegistry, RenameProvider, RenameLocation, Rejection } from 'vs/editor/common/modes'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { alert } from 'vs/base/browser/ui/aria/aria'; @@ -23,17 +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, @@ -47,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; + } + + 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 + }; + } - return res; + async provideRenameEdits(newName: string, token: CancellationToken): Promise { + return this._provideRenameEdits(newName, this._providerRenameIdx, [], token); } - async provideRenameEdits(newName: string, i: number, rejects: string[], token: CancellationToken): Promise { + private async _provideRenameEdits(newName: string, i: number, rejects: string[], token: CancellationToken): Promise { const provider = this._providers[i]; if (!provider) { return { @@ -81,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 @@ -109,13 +134,14 @@ class RenameController implements IEditorContribution { constructor( private readonly editor: ICodeEditor, + @IInstantiationService private readonly _instaService: IInstantiationService, @INotificationService private readonly _notificationService: INotificationService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IEditorProgressService private readonly _progressService: IEditorProgressService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @IThemeService private readonly _themeService: IThemeService, + @ILogService private readonly _logService: ILogService, + @ITextResourceConfigurationService private readonly _configService: ITextResourceConfigurationService, ) { - this._renameInputField = new IdleValue(() => this._dispoableStore.add(new RenameInputField(this.editor, this._themeService, this._contextKeyService))); + this._renameInputField = this._dispoableStore.add(new IdleValue(() => this._dispoableStore.add(this._instaService.createInstance(RenameInputField, this.editor, ['acceptRenameInput', 'acceptRenameInputWithPreview'])))); } dispose(): void { @@ -163,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(); @@ -174,11 +202,12 @@ class RenameController implements IEditorContribution { selectionEnd = Math.min(loc.range.endColumn, selection.endColumn) - loc.range.startColumn; } - const newNameOrFocusFlag = await this._renameInputField.getValue().getInput(loc.range, loc.text, selectionStart, selectionEnd); + 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); - - if (typeof newNameOrFocusFlag === 'boolean') { - if (newNameOrFocusFlag) { + // no result, only hint to focus the editor or not + if (typeof inputFieldResult === 'boolean') { + if (inputFieldResult) { this.editor.focus(); } return undefined; @@ -186,7 +215,7 @@ class RenameController implements IEditorContribution { this.editor.focus(); - const renameOperation = raceCancellation(skeleton.provideRenameEdits(newNameOrFocusFlag, 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; @@ -197,16 +226,23 @@ class RenameController implements IEditorContribution { return; } - const editResult = await this._bulkEditService.apply(renameResult, { editor: this.editor }); - - // alert - if (editResult.ariaSummary) { - alert(nls.localize('aria', "Successfully renamed '{0}' to '{1}'. Summary: {2}", loc!.text, newNameOrFocusFlag, editResult.ariaSummary)); - } + this._bulkEditService.apply(ResourceEdit.convert(renameResult), { + editor: this.editor, + showPreview: inputFieldResult.wantsPreview, + 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)); + } + }).catch(err => { + this._notificationService.error(nls.localize('rename.failedApply', "Rename failed to apply edits")); + this._logService.error(err); + }); }, err => { - this._notificationService.error(nls.localize('rename.failed', "Rename failed to execute.")); - return Promise.reject(err); + this._notificationService.error(nls.localize('rename.failed', "Rename failed to compute edits")); + this._logService.error(err); }); this._progressService.showWhile(renameOperation, 250); @@ -214,12 +250,12 @@ class RenameController implements IEditorContribution { } - acceptRenameInput(): void { - this._renameInputField.getValue().acceptInput(); + acceptRenameInput(wantsPreview: boolean): void { + this._renameInputField.value.acceptInput(wantsPreview); } cancelRenameInput(): void { - this._renameInputField.getValue().cancelInput(true); + this._renameInputField.value.cancelInput(true); } } @@ -238,7 +274,7 @@ export class RenameAction extends EditorAction { primary: KeyCode.F2, weight: KeybindingWeight.EditorContrib }, - menuOpts: { + contextMenuOpts: { group: '1_modification', order: 1.1 } @@ -282,7 +318,7 @@ const RenameCommand = EditorCommand.bindToContribution(RenameC registerEditorCommand(new RenameCommand({ id: 'acceptRenameInput', precondition: CONTEXT_RENAME_INPUT_VISIBLE, - handler: x => x.acceptRenameInput(), + handler: x => x.acceptRenameInput(false), kbOpts: { weight: KeybindingWeight.EditorContrib + 99, kbExpr: EditorContextKeys.focus, @@ -290,6 +326,17 @@ registerEditorCommand(new RenameCommand({ } })); +registerEditorCommand(new RenameCommand({ + id: 'acceptRenameInputWithPreview', + precondition: ContextKeyExpr.and(CONTEXT_RENAME_INPUT_VISIBLE, ContextKeyExpr.has('config.editor.rename.enablePreview')), + handler: x => x.acceptRenameInput(true), + kbOpts: { + weight: KeybindingWeight.EditorContrib + 99, + kbExpr: EditorContextKeys.focus, + primary: KeyMod.Shift + KeyCode.Enter + } +})); + registerEditorCommand(new RenameCommand({ id: 'cancelRenameInput', precondition: CONTEXT_RENAME_INPUT_VISIBLE, @@ -304,10 +351,22 @@ 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 +Registry.as(Extensions.Configuration).registerConfiguration({ + id: 'editor', + properties: { + 'editor.rename.enablePreview': { + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, + description: nls.localize('enablePreview', "Enable/disable the ability to preview changes before renaming"), + default: true, + type: 'boolean' + } + } +}); diff --git a/src/vs/editor/contrib/rename/renameInputField.css b/src/vs/editor/contrib/rename/renameInputField.css index 0478cd7ce48c3..99757bf38a4c3 100644 --- a/src/vs/editor/contrib/rename/renameInputField.css +++ b/src/vs/editor/contrib/rename/renameInputField.css @@ -8,6 +8,20 @@ color: inherit; } +.monaco-editor .rename-box.preview { + padding: 3px 3px 0 3px; +} + .monaco-editor .rename-box .rename-input { - padding: 4px; + padding: 3px; + width: calc(100% - 6px); +} + +.monaco-editor .rename-box .rename-label { + display: none; + opacity: .8; +} + +.monaco-editor .rename-box.preview .rename-label { + display: inherit; } diff --git a/src/vs/editor/contrib/rename/renameInputField.ts b/src/vs/editor/contrib/rename/renameInputField.ts index a87899a71a1ff..2be443c8c5531 100644 --- a/src/vs/editor/contrib/rename/renameInputField.ts +++ b/src/vs/editor/contrib/rename/renameInputField.ts @@ -4,186 +4,195 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./renameInputField'; -import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +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 } from 'vs/platform/theme/common/colorRegistry'; -import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { inputBackground, inputBorder, inputForeground, widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry'; +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 { CancellationToken } from 'vs/base/common/cancellation'; export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey('renameInputVisible', false); -export class RenameInputField implements IContentWidget, IDisposable { +export interface RenameInputFieldResult { + newName: string; + wantsPreview?: boolean; +} + +export class RenameInputField implements IContentWidget { - private _editor: ICodeEditor; private _position?: Position; private _domNode?: HTMLElement; - private _inputField?: HTMLInputElement; + private _input?: HTMLInputElement; + private _label?: HTMLDivElement; private _visible?: boolean; private readonly _visibleContextKey: IContextKey; private readonly _disposables = new DisposableStore(); - // Editor.IContentWidget.allowEditorOverflow - allowEditorOverflow: boolean = true; + readonly allowEditorOverflow: boolean = true; constructor( - editor: ICodeEditor, - private readonly themeService: IThemeService, - contextKeyService: IContextKeyService, + private readonly _editor: ICodeEditor, + private readonly _acceptKeybindings: [string, string], + @IThemeService private readonly _themeService: IThemeService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IContextKeyService contextKeyService: IContextKeyService, ) { this._visibleContextKey = CONTEXT_RENAME_INPUT_VISIBLE.bindTo(contextKeyService); - this._editor = editor; this._editor.addContentWidget(this); - this._disposables.add(editor.onDidChangeConfiguration(e => { + this._disposables.add(this._editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.fontInfo)) { - this.updateFont(); + this._updateFont(); } })); - this._disposables.add(themeService.onThemeChange(theme => this.onThemeChange(theme))); - } - - private onThemeChange(theme: ITheme): void { - this.updateStyles(theme); + this._disposables.add(_themeService.onDidColorThemeChange(this._updateStyles, this)); } - public dispose(): void { + dispose(): void { this._disposables.dispose(); this._editor.removeContentWidget(this); } - public getId(): string { + getId(): string { return '__renameInputWidget'; } - public getDomNode(): HTMLElement { + getDomNode(): HTMLElement { if (!this._domNode) { - this._inputField = document.createElement('input'); - this._inputField.className = 'rename-input'; - this._inputField.type = 'text'; - this._inputField.setAttribute('aria-label', localize('renameAriaLabel', "Rename input. Type new name and press Enter to commit.")); this._domNode = document.createElement('div'); - this._domNode.style.height = `${this._editor.getOption(EditorOption.lineHeight)}px`; this._domNode.className = 'monaco-editor rename-box'; - this._domNode.appendChild(this._inputField); - this.updateFont(); - this.updateStyles(this.themeService.getTheme()); + this._input = document.createElement('input'); + this._input.className = 'rename-input'; + this._input.type = 'text'; + this._input.setAttribute('aria-label', localize('renameAriaLabel', "Rename input. Type new name and press Enter to commit.")); + this._domNode.appendChild(this._input); + + this._label = document.createElement('div'); + this._label.className = 'rename-label'; + this._domNode.appendChild(this._label); + const updateLabel = () => { + const [accept, preview] = this._acceptKeybindings; + this._keybindingService.lookupKeybinding(accept); + 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.getColorTheme()); } return this._domNode; } - private updateStyles(theme: ITheme): void { - if (!this._inputField) { + private _updateStyles(theme: IColorTheme): void { + if (!this._input || !this._domNode) { return; } - const background = theme.getColor(inputBackground); - const foreground = theme.getColor(inputForeground); const widgetShadowColor = theme.getColor(widgetShadow); - const border = theme.getColor(inputBorder); - - this._inputField.style.backgroundColor = background ? background.toString() : ''; - this._inputField.style.color = foreground ? foreground.toString() : null; - - this._inputField.style.borderWidth = border ? '1px' : '0px'; - this._inputField.style.borderStyle = border ? 'solid' : 'none'; - this._inputField.style.borderColor = border ? border.toString() : 'none'; + this._domNode.style.backgroundColor = String(theme.getColor(editorWidgetBackground) ?? ''); + this._domNode.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : ''; + this._domNode.style.color = String(theme.getColor(inputForeground) ?? ''); - this._domNode!.style.boxShadow = widgetShadowColor ? ` 0 2px 8px ${widgetShadowColor}` : ''; + this._input.style.backgroundColor = String(theme.getColor(inputBackground) ?? ''); + // this._input.style.color = String(theme.getColor(inputForeground) ?? ''); + const border = theme.getColor(inputBorder); + this._input.style.borderWidth = border ? '1px' : '0px'; + this._input.style.borderStyle = border ? 'solid' : 'none'; + this._input.style.borderColor = border?.toString() ?? 'none'; } - private updateFont(): void { - if (!this._inputField) { + private _updateFont(): void { + if (!this._input || !this._label) { return; } const fontInfo = this._editor.getOption(EditorOption.fontInfo); - this._inputField.style.fontFamily = fontInfo.fontFamily; - this._inputField.style.fontWeight = fontInfo.fontWeight; - this._inputField.style.fontSize = `${fontInfo.fontSize}px`; + this._input.style.fontFamily = fontInfo.fontFamily; + this._input.style.fontWeight = fontInfo.fontWeight; + this._input.style.fontSize = `${fontInfo.fontSize}px`; + + this._label.style.fontSize = `${fontInfo.fontSize * 0.8}px`; } - public getPosition(): IContentWidgetPosition | null { - return this._visible - ? { position: this._position!, preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] } - : null; + getPosition(): IContentWidgetPosition | null { + if (!this._visible) { + return null; + } + return { + position: this._position!, + preference: [ContentWidgetPositionPreference.BELOW, ContentWidgetPositionPreference.ABOVE] + }; } - private _currentAcceptInput: (() => void) | null = null; - private _currentCancelInput: ((focusEditor: boolean) => void) | null = null; + private _currentAcceptInput?: (wantsPreview: boolean) => void; + private _currentCancelInput?: (focusEditor: boolean) => void; - public acceptInput(): void { + acceptInput(wantsPreview: boolean): void { if (this._currentAcceptInput) { - this._currentAcceptInput(); + this._currentAcceptInput(wantsPreview); } } - public cancelInput(focusEditor: boolean): void { + cancelInput(focusEditor: boolean): void { if (this._currentCancelInput) { this._currentCancelInput(focusEditor); } } - public getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number): Promise { + getInput(where: IRange, value: string, selectionStart: number, selectionEnd: number, supportPreview: boolean, token: CancellationToken): Promise { + + this._domNode!.classList.toggle('preview', supportPreview); this._position = new Position(where.startLineNumber, where.startColumn); - this._inputField!.value = value; - this._inputField!.setAttribute('selectionStart', selectionStart.toString()); - this._inputField!.setAttribute('selectionEnd', selectionEnd.toString()); - this._inputField!.size = Math.max((where.endColumn - where.startColumn) * 1.1, 20); + this._input!.value = value; + this._input!.setAttribute('selectionStart', selectionStart.toString()); + this._input!.setAttribute('selectionEnd', selectionEnd.toString()); + this._input!.size = Math.max((where.endColumn - where.startColumn) * 1.1, 20); const disposeOnDone = new DisposableStore(); - const always = () => { - disposeOnDone.dispose(); - this._hide(); - }; - return new Promise(resolve => { + return new Promise(resolve => { this._currentCancelInput = (focusEditor) => { - this._currentAcceptInput = null; - this._currentCancelInput = null; + this._currentAcceptInput = undefined; + this._currentCancelInput = undefined; resolve(focusEditor); return true; }; - this._currentAcceptInput = () => { - if (this._inputField!.value.trim().length === 0 || this._inputField!.value === value) { + this._currentAcceptInput = (wantsPreview) => { + if (this._input!.value.trim().length === 0 || this._input!.value === value) { // empty or whitespace only or not changed this.cancelInput(true); return; } - this._currentAcceptInput = null; - this._currentCancelInput = null; - resolve(this._inputField!.value); + this._currentAcceptInput = undefined; + this._currentCancelInput = undefined; + resolve({ + newName: this._input!.value, + wantsPreview: supportPreview && wantsPreview + }); }; - 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(); - }).then(newValue => { - always(); - return newValue; - }, err => { - always(); - return Promise.reject(err); + }).finally(() => { + disposeOnDone.dispose(); + this._hide(); }); } @@ -194,10 +203,10 @@ export class RenameInputField implements IContentWidget, IDisposable { this._editor.layoutContentWidget(this); setTimeout(() => { - this._inputField!.focus(); - this._inputField!.setSelectionRange( - parseInt(this._inputField!.getAttribute('selectionStart')!), - parseInt(this._inputField!.getAttribute('selectionEnd')!)); + this._input!.focus(); + this._input!.setSelectionRange( + parseInt(this._input!.getAttribute('selectionStart')!), + parseInt(this._input!.getAttribute('selectionEnd')!)); }, 100); } 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 0000000000000..05dba5e4b6241 --- /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 69bbb6eadeaef..3842ad87b549b 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 81b60e73d91c6..b6de17c9c99f0 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -7,7 +7,7 @@ import * as arrays from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, IActionOptions, registerEditorAction, registerEditorContribution, ServicesAccessor, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { EditorAction, IActionOptions, registerEditorAction, registerEditorContribution, ServicesAccessor, registerModelCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -18,7 +18,7 @@ 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'; @@ -64,7 +64,7 @@ class SmartSelectController implements IEditorContribution { } dispose(): void { - dispose(this._selectionListener); + this._selectionListener?.dispose(); } run(forward: boolean): Promise | void { @@ -106,10 +106,10 @@ 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; } }); @@ -167,7 +167,7 @@ class GrowSelectionAction extends AbstractSmartSelect { }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '1_basic', title: nls.localize({ key: 'miSmartSelectGrow', comment: ['&& denotes a mnemonic'] }, "&&Expand Selection"), @@ -196,7 +196,7 @@ class ShrinkSelectionAction extends AbstractSmartSelect { }, weight: KeybindingWeight.EditorContrib }, - menubarOpts: { + menuOpts: { menuId: MenuId.MenubarSelectionMenu, group: '1_basic', title: nls.localize({ key: 'miSmartSelectShrink', comment: ['&& denotes a mnemonic'] }, "&&Shrink Selection"), @@ -302,6 +302,7 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], }); } -registerDefaultLanguageCommand('_executeSelectionRangeProvider', function (model, _position, args) { - return provideSelectionRanges(model, args.positions, CancellationToken.None); +registerModelCommand('_executeSelectionRangeProvider', function (model, ...args) { + const [positions] = args; + return provideSelectionRanges(model, positions, 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 f0eedb8324d39..5ca1c5b592724 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -17,6 +17,11 @@ import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelec import { CancellationToken } from 'vs/base/common/cancellation'; import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections'; 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 { @@ -45,7 +50,8 @@ suite('SmartSelect', () => { setup(() => { const configurationService = new TestConfigurationService(); - modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService)); + const dialogService = new TestDialogService(); + modelService = new ModelServiceImpl(configurationService, new TestTextResourcePropertiesService(configurationService), new TestThemeService(), new NullLogService(), new UndoRedoService(dialogService, new TestNotificationService())); mode = new MockJSMode(); }); @@ -178,9 +184,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]; @@ -195,18 +203,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 @@ -217,23 +225,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), ); @@ -242,60 +250,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), ); }); @@ -319,4 +327,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 7402202af549e..280663f59a895 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 322b36075cab0..6459f4402f2ba 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 7823405f14e08..b6bd2eb37d36d 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 }; @@ -605,7 +609,7 @@ export class SnippetParser { // fill in values for placeholders. the first placeholder of an index // that has a value defines the value for all placeholders with that index - const placeholderDefaultValues = new Map(); + const placeholderDefaultValues = new Map(); const incompletePlaceholders: Placeholder[] = []; let placeholderCount = 0; snippet.walk(marker => { diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 8af3c14063be3..7f91f835c4c7a 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,7 @@ 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'; registerThemingParticipant((theme, collector) => { @@ -65,9 +65,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 +168,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 +303,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 +320,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 { @@ -385,7 +385,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 +396,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 +452,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 +466,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 +499,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 +519,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 +530,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 +599,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 +607,7 @@ export class SnippetSession { break; } } - }); + } } if (allPossibleSelections.size === 0) { @@ -624,11 +628,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 +639,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 09fd3ca980616..cd5d505bcc906 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 05db9b1f12b23..8c9be33cf114b 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 e2e7d2d023dd3..c430c2638572f 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,22 @@ 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 () { + this.skip(); + 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'); + }); }); diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index d4ae07a4a49db..f72727ccdce6b 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 06eb5f8777c13..658039f80650d 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'); }); @@ -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 c1349fa7935e8..8926c628e435e 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -41,6 +41,9 @@ const enum Refilter { Incr = 2 } +/** + * Sorted, filtered completion view model + * */ export class CompletionModel { private readonly _items: CompletionItem[]; @@ -61,7 +64,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; @@ -101,7 +105,7 @@ export class CompletionModel { } 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]); @@ -151,6 +155,10 @@ export class CompletionModel { const item = source[i]; + if (item.isInvalid) { + continue; // SKIP invalid items + } + // collect those supports that signaled having // an incomplete result if (item.container.incomplete) { @@ -192,6 +200,7 @@ 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 @@ -206,19 +215,19 @@ export class CompletionModel { if (!match) { continue; // NO match } - if (compareIgnoreCase(item.completion.filterText, item.completion.label) === 0) { + if (compareIgnoreCase(item.completion.filterText, textLabel) === 0) { // filterText and label are actually the same -> use good highlights item.score = match; } else { // re-run the scorer on the label in the hope of a result BUT use the rank // of the filterText-match - item.score = anyScore(word, wordLow, wordPos, item.completion.label, item.labelLow, 0); + item.score = anyScore(word, wordLow, wordPos, textLabel, item.labelLow, 0); item.score[0] = match[0]; // use score from filterText } } else { // by default match `word` against the `label` - let match = scoreFn(word, wordLow, wordPos, item.completion.label, item.labelLow, 0, false); + let match = scoreFn(word, wordLow, wordPos, textLabel, item.labelLow, 0, false); if (!match) { continue; // NO match } diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index a58882341b8b9..d7fb21e915b66 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -99,20 +99,61 @@ overflow: hidden; text-overflow: ellipsis; white-space: pre; + 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 { + display: flex; } .monaco-editor .suggest-widget:not(.frozen) .monaco-highlighted-label .highlight { font-weight: bold; } -/** Icon styles **/ +/** Status Bar **/ + +.monaco-editor .suggest-widget > .suggest-status-bar { + visibility: hidden; + + position: absolute; + left: 0; + + box-sizing: border-box; + + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + + width: 100%; + + font-size: 80%; + + border-left-width: 1px; + border-left-style: solid; + border-right-width: 1px; + border-right-style: solid; + border-bottom-width: 1px; + border-bottom-style: solid; + + padding: 0 8px 0 4px; +} + +.monaco-editor .suggest-widget.list-right.docs-side > .suggest-status-bar { + left: auto; + right: 0; +} +.monaco-editor .suggest-widget.docs-side > .suggest-status-bar { + width: 50%; +} + +/** 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 > .readMore::before { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore::before { color: inherit; - opacity: 0.6; + opacity: 1; font-size: 14px; - margin-left: 4px; cursor: pointer; } @@ -123,46 +164,129 @@ } .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close:hover, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore: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 { + opacity: 0.7; +} + +.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 > .type-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label { margin-left: 0.8em; - flex: 1; - text-align: right; overflow: hidden; text-overflow: ellipsis; - opacity: 0.7; white-space: nowrap; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label > .monaco-tokenized-source { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label > .monaco-tokenized-source { display: inline; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label, -.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { +/** 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 { display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { +.monaco-editor .suggest-widget .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: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 { + width: calc(100% - 26px); +} + +.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: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; + margin-left: 16px; + flex-shrink: 0; + max-width: 45%; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore { + display: inline-block; + position: absolute; + right: 10px; + width: 18px; + height: 18px; + visibility: hidden; +} + +/** 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 { + 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.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.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 { + display: none; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row:hover > .contents > .main > .right > .readMore { + visibility: visible; +} + /** Styles for each row in the list **/ .monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated { opacity: 0.66; text-decoration: unset; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated > .monaco-icon-label-description-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; } @@ -227,6 +351,7 @@ box-sizing: border-box; height: 100%; width: 100%; + padding-right: 22px; } .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .type { @@ -248,6 +373,7 @@ .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs { padding: 0; white-space: initial; + min-height: calc(1rem + 8px); } .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs > div, @@ -268,6 +394,10 @@ word-wrap: break-word; } +.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .docs.markdown-docs .codicon { + vertical-align: sub; +} + .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > p:empty { display: none; } @@ -276,3 +406,11 @@ border-radius: 3px; padding: 0 0.4em; } + + +/* replace/insert decorations */ + +.monaco-editor .suggest-insert-unexpected { + font-style: italic; +} + diff --git a/src/vs/editor/contrib/suggest/media/suggestStatusBar.css b/src/vs/editor/contrib/suggest/media/suggestStatusBar.css new file mode 100644 index 0000000000000..f645f3c0240c1 --- /dev/null +++ b/src/vs/editor/contrib/suggest/media/suggestStatusBar.css @@ -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. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar { + visibility: visible; +} +.monaco-editor .suggest-widget.with-status-bar > .tree { + margin-bottom: 18px; +} + +.monaco-editor .suggest-widget.with-status-bar .suggest-status-bar .action-label { + min-height: 18px; + opacity: 0.5; + color: inherit; +} + +.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%; +} diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index d28423e1602f1..ff1e75b7d9e3f 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,46 +14,68 @@ 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'; 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), + CanResolve: new RawContextKey('suggestionCanResolve', false), }; +export const suggestWidgetStatusbarMenu = new MenuId('suggestWidgetStatusBar'); + export class CompletionItem { _brand!: 'ISuggestionItem'; - readonly resolve: (token: CancellationToken) => Promise; - // readonly editStart: IPosition; readonly editInsertEnd: IPosition; readonly editReplaceEnd: IPosition; + // + readonly textLabel: string; + // perf readonly labelLow: string; 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 + : completion.label.name; + // ensure lower-variants (perf) - this.labelLow = completion.label.toLowerCase(); + 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(); @@ -64,42 +84,56 @@ 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(); - } else { - let cached: Promise | undefined; - this.resolve = (token) => { - if (!cached) { - let isDone = false; - cached = Promise.resolve(resolveCompletionItem.call(provider, model, position, completion, token)).then(value => { - assign(completion, value); - isDone = true; - }, err => { - if (isPromiseCanceledError(err)) { - // the IPC queue will reject the request with the - // cancellation error -> reset cached - cached = undefined; - } - }); - token.onCancellationRequested(() => { - if (!isDone) { - // cancellation after the request has been - // dispatched -> reset cache - cached = undefined; - } - }); + 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 cached; - }; + }); } + return this._resolveCache; } } @@ -130,97 +164,108 @@ export function setSnippetSuggestSupport(support: modes.CompletionItemProvider): return old; } -export function provideSuggestionItems( +class CompletionItemModel { + constructor( + readonly items: CompletionItem[], + readonly needsClipboard: boolean, + readonly dispoables: 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 t1 = Date.now(); + 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 wordUntil = model.getWordUntilPosition(position); - // const defaultRange = new Range(position.lineNumber, wordUntil.startColumn, position.lineNumber, wordUntil.endColumn); - - position = position.clone(); + const defaultRange = { replace: defaultReplaceRange, insert: defaultReplaceRange.setEndPosition(position.lineNumber, position.column) }; - // get provider groups, always add snippet suggestion provider - const supports = modes.CompletionProviderRegistry.orderedGroups(model); + const result: CompletionItem[] = []; + const disposables = new DisposableStore(); + let needsClipboard = false; - // add snippets provider unless turned off - if (!options.kindFilter.has(modes.CompletionItemKind.Snippet) && _snippetSuggestSupport) { - supports.unshift([_snippetSuggestSupport]); - } + const onCompletionList = (provider: modes.CompletionItemProvider, container: modes.CompletionList | null | undefined) => { + 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); + } + }; - const allSuggestions: CompletionItem[] = []; - const disposables = new DisposableStore(); - let hasResult = false; + // 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 list = await _snippetSuggestSupport.provideCompletionItems(model, position, context, token); + onCompletionList(_snippetSuggestSupport, list); + })(); // 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 list = await provider.provideCompletionItems(model, position, context, token); + onCompletionList(provider, list); + } 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 = suggestion.label; - } - - 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()); + } + // console.log(`${result.length} items AFTER ${Date.now() - t1}ms`); + return new CompletionItemModel( + result.sort(getSuggestionComparator(options.snippetSortOrder)), + needsClipboard, + disposables + ); } @@ -282,27 +327,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.dispoables.dispose(), 100); } }); diff --git a/src/vs/editor/contrib/suggest/suggestAlternatives.ts b/src/vs/editor/contrib/suggest/suggestAlternatives.ts index 6c9a148e618a7..7c72d74d4f303 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/suggestCommitCharacters.ts b/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts index 3831500a4f861..54859bf35e5a0 100644 --- a/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts +++ b/src/vs/editor/contrib/suggest/suggestCommitCharacters.ts @@ -26,7 +26,7 @@ export class CommitCharacterController { this._disposables.add(widget.onDidHide(this.reset, this)); this._disposables.add(editor.onWillType(text => { - if (this._active) { + if (this._active && !widget.isFrozen()) { const ch = text.charCodeAt(text.length - 1); if (this._active.acceptCharacters.has(ch) && editor.getOption(EditorOption.acceptSuggestionOnCommitCharacter)) { accept(this._active.item); diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 007b89d720391..58739e3d3f33a 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,7 +24,7 @@ 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'; @@ -31,18 +32,22 @@ 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 } from 'vs/base/common/types'; +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 { 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'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -/** - * 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 { @@ -101,55 +106,71 @@ export class SuggestController implements IEditorContribution { return editor.getContribution(SuggestController.ID); } - private readonly _model: SuggestModel; - private readonly _widget: IdleValue; + readonly editor: ICodeEditor; + readonly model: SuggestModel; + readonly widget: IdleValue; + private readonly _alternatives: IdleValue; private readonly _lineSuffix = new MutableDisposable(); private readonly _toDispose = new DisposableStore(); + private readonly _overtypingCapturer: IdleValue; constructor( - private _editor: ICodeEditor, + 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, + @IClipboardService clipboardService: IClipboardService, ) { - this._model = new SuggestModel(this._editor, editorWorker); + this.editor = editor; + this.model = new SuggestModel(this.editor, editorWorker, clipboardService); - this._widget = new IdleValue(() => { + this.widget = this._toDispose.add(new IdleValue(() => { - const widget = this._instantiationService.createInstance(SuggestWidget, this._editor); + const widget = this._instantiationService.createInstance(SuggestWidget, this.editor); this._toDispose.add(widget); this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, 0), this)); // Wire up logic to accept a suggestion on certain characters - const commitCharacterController = new CommitCharacterController(this._editor, widget, item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop)); + const commitCharacterController = new CommitCharacterController(this.editor, widget, item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop)); this._toDispose.add(commitCharacterController); - this._toDispose.add(this._model.onDidSuggest(e => { + this._toDispose.add(this.model.onDidSuggest(e => { if (e.completionModel.items.length === 0) { commitCharacterController.reset(); } })); // 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 }) => { - const position = this._editor.getPosition()!; + // (ctx: makesTextEdit) + const position = this.editor.getPosition()!; const startColumn = item.editStart.column; const endColumn = position.column; let value = true; if ( - this._editor.getOption(EditorOption.acceptSuggestionOnEnter) === 'smart' - && this._model.state === State.Auto + this.editor.getOption(EditorOption.acceptSuggestionOnEnter) === 'smart' + && this.model.state === State.Auto && !item.completion.command && !item.completion.additionalTextEdits && !(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet) && endColumn - startColumn === item.completion.insertText.length ) { - const oldText = this._editor.getModel()!.getValueInRange({ + const oldText = this.editor.getModel()!.getValueInRange({ startLineNumber: position.lineNumber, startColumn, endLineNumber: position.lineNumber, @@ -157,71 +178,81 @@ 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(widget.onDetailsKeyDown(e => { + // cmd + c on macOS, ctrl + c on Win / Linux + if ( + e.toKeybinding().equals(new SimpleKeybinding(true, false, false, false, KeyCode.KEY_C)) || + (platform.isMacintosh && e.toKeybinding().equals(new SimpleKeybinding(false, false, false, true, KeyCode.KEY_C))) + ) { + e.stopPropagation(); + return; + } + + if (!e.toKeybinding().isModifierKey()) { + this.editor.focus(); + } })); - this._toDispose.add(toDisposable(() => makesTextEdit.reset())); return widget; - }); + })); - this._alternatives = new IdleValue(() => { - return this._toDispose.add(new SuggestAlternatives(this._editor, this._contextKeyService)); - }); + // 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)); + })); - this._toDispose.add(_instantiationService.createInstance(WordContextKey, _editor)); + 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._lineSuffix.value = new LineSuffix(this._editor.getModel()!, e.position); + this._toDispose.add(this.model.onDidTrigger(e => { + 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 => { + 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); + let index = this._memoryService.select(this.editor.getModel()!, this.editor.getPosition()!, e.completionModel.items); + this.widget.value.showSuggestions(e.completionModel, index, e.isFrozen, e.auto); } })); - this._toDispose.add(this._model.onDidCancel(e => { + this._toDispose.add(this.model.onDidCancel(e => { if (!e.retrigger) { - this._widget.getValue().hideWidget(); + this.widget.value.hideWidget(); } })); - this._toDispose.add(this._editor.onDidBlurEditorWidget(() => { + this._toDispose.add(this.editor.onDidBlurEditorWidget(() => { if (!_sticky) { - this._model.cancel(); - this._model.clear(); - } - })); - - this._toDispose.add(this._widget.getValue().onDetailsKeyDown(e => { - // cmd + c on macOS, ctrl + c on Win / Linux - if ( - e.toKeybinding().equals(new SimpleKeybinding(true, false, false, false, KeyCode.KEY_C)) || - (platform.isMacintosh && e.toKeybinding().equals(new SimpleKeybinding(false, false, false, true, KeyCode.KEY_C))) - ) { - e.stopPropagation(); - return; - } - - if (!e.toKeybinding().isModifierKey()) { - this._editor.focus(); + this.model.cancel(); + this.model.clear(); } })); // Manage the acceptSuggestionsOnEnter context key let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService); let updateFromConfig = () => { - const acceptSuggestionOnEnter = this._editor.getOption(EditorOption.acceptSuggestionOnEnter); + const acceptSuggestionOnEnter = this.editor.getOption(EditorOption.acceptSuggestionOnEnter); acceptSuggestionsOnEnter.set(acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart'); }; - this._toDispose.add(this._editor.onDidChangeConfiguration(() => updateFromConfig())); + this._toDispose.add(this.editor.onDidChangeConfiguration(() => updateFromConfig())); updateFromConfig(); } dispose(): void { this._alternatives.dispose(); this._toDispose.dispose(); - this._widget.dispose(); - this._model.dispose(); + this.widget.dispose(); + this.model.dispose(); this._lineSuffix.dispose(); } @@ -230,78 +261,141 @@ export class SuggestController implements IEditorContribution { flags: InsertFlags ): void { if (!event || !event.item) { - this._alternatives.getValue().reset(); - this._model.cancel(); - this._model.clear(); + this._alternatives.value.reset(); + this.model.cancel(); + this.model.clear(); return; } - if (!this._editor.hasModel()) { + if (!this.editor.hasModel()) { return; } - const model = this._editor.getModel(); + const model = this.editor.getModel(); const modelVersionNow = model.getAlternativeVersionId(); const { item } = event; - const { completion: suggestion, position } = item; - const columnDelta = this._editor.getPosition().column - position.column; + + // + const tasks: Promise[] = []; + const cts = new CancellationTokenSource(); // pushing undo stops *before* additional text edits and // *after* the main edit if (!(flags & InsertFlags.NoBeforeUndoStop)) { - this._editor.pushUndoStop(); + this.editor.pushUndoStop(); } - if (Array.isArray(suggestion.additionalTextEdits)) { - this._editor.executeEdits('suggestController.additionalTextEdits', suggestion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); - } + // compute overwrite[Before|After] deltas BEFORE applying extra edits + const info = this.getOverwriteInfo(item, Boolean(flags & InsertFlags.AlternativeOverwriteConfig)); // keep item in memory - this._memoryService.memorize(model, this._editor.getPosition(), item); + this._memoryService.memorize(model, this.editor.getPosition(), item); + + + 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 { insertText } = suggestion; - if (!(suggestion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) { - insertText = SnippetParser.escape(insertText); - } + 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(); + } + }); - const overwriteConfig = flags & InsertFlags.AlternativeOverwriteConfig - ? !this._editor.getOption(EditorOption.suggest).overwriteOnAccept - : this._editor.getOption(EditorOption.suggest).overwriteOnAccept; + 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(); + })); + } - const overwriteBefore = position.column - item.editStart.column; - const overwriteAfter = (overwriteConfig ? item.editReplaceEnd.column : item.editInsertEnd.column) - position.column; - const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this._editor.getPosition()) : 0; + let { insertText } = item.completion; + if (!(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) { + insertText = SnippetParser.escape(insertText); + } - SnippetController2.get(this._editor).insert(insertText, { - overwriteBefore: overwriteBefore + columnDelta, - overwriteAfter: overwriteAfter + suffixDelta, + SnippetController2.get(this.editor).insert(insertText, { + overwriteBefore: info.overwriteBefore, + 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(); + this.editor.pushUndoStop(); } - if (!suggestion.command) { + if (!item.completion.command) { // done - this._model.cancel(); - this._model.clear(); + this.model.cancel(); - } else if (suggestion.command.id === TriggerSuggestAction.id) { + } else if (item.completion.command.id === TriggerSuggestAction.id) { // retigger - this._model.trigger({ auto: true, shy: false }, true); + 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 - this._model.cancel(); + 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... @@ -318,33 +412,58 @@ 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 } { + assertType(this.editor.hasModel()); + + let replace = this.editor.getOption(EditorOption.suggest).insertMode === 'replace'; + if (toggleMode) { + replace = !replace; + } + const overwriteBefore = item.position.column - item.editStart.column; + const overwriteAfter = (replace ? item.editReplaceEnd.column : item.editInsertEnd.column) - item.position.column; + const columnDelta = this.editor.getPosition().column - item.position.column; + const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this.editor.getPosition()) : 0; + + return { + overwriteBefore: overwriteBefore + columnDelta, + overwriteAfter: overwriteAfter + suffixDelta + }; } 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", suggestion.label, suggestion.additionalTextEdits.length); + let msg = nls.localize('aria.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length); alert(msg); } } triggerSuggest(onlyFrom?: Set): void { - if (this._editor.hasModel()) { - this._model.trigger({ auto: false, shy: false }, false, onlyFrom); - this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth); - this._editor.focus(); + if (this.editor.hasModel()) { + this.model.trigger({ auto: false, shy: false }, false, onlyFrom); + this.editor.revealLine(this.editor.getPosition().lineNumber, ScrollType.Smooth); + this.editor.focus(); } } triggerSuggestAndAcceptBest(arg: { fallback: string }): void { - if (!this._editor.hasModel()) { + if (!this.editor.hasModel()) { return; } - const positionNow = this._editor.getPosition(); + const positionNow = this.editor.getPosition(); const fallback = () => { - if (positionNow.equals(this._editor.getPosition()!)) { + if (positionNow.equals(this.editor.getPosition()!)) { this._commandService.executeCommand(arg.fallback); } }; @@ -354,14 +473,14 @@ export class SuggestController implements IEditorContribution { // snippet, other editor -> makes edit return true; } - const position = this._editor.getPosition()!; + const position = this.editor.getPosition()!; const startColumn = item.editStart.column; const endColumn = position.column; if (endColumn - startColumn !== item.completion.insertText.length) { // unequal lengths -> makes edit return true; } - const textNow = this._editor.getModel()!.getValueInRange({ + const textNow = this.editor.getModel()!.getValueInRange({ startLineNumber: position.lineNumber, startColumn, endLineNumber: position.lineNumber, @@ -371,41 +490,41 @@ export class SuggestController implements IEditorContribution { return textNow !== item.completion.insertText; }; - Event.once(this._model.onDidTrigger)(_ => { + Event.once(this.model.onDidTrigger)(_ => { // wait for trigger because only then the cancel-event is trustworthy let listener: IDisposable[] = []; - Event.any(this._model.onDidTrigger, this._model.onDidCancel)(() => { + Event.any(this.model.onDidTrigger, this.model.onDidCancel)(() => { // retrigger or cancel -> try to type default text dispose(listener); fallback(); }, undefined, listener); - this._model.onDidSuggest(({ completionModel }) => { + this.model.onDidSuggest(({ completionModel }) => { dispose(listener); if (completionModel.items.length === 0) { fallback(); return; } - const index = this._memoryService.select(this._editor.getModel()!, this._editor.getPosition()!, completionModel.items); + const index = this._memoryService.select(this.editor.getModel()!, this.editor.getPosition()!, completionModel.items); const item = completionModel.items[index]; if (!makesTextEdit(item)) { fallback(); return; } - this._editor.pushUndoStop(); + this.editor.pushUndoStop(); this._insertSuggestion({ index, item, model: completionModel }, InsertFlags.KeepAlternativeSuggestions | InsertFlags.NoBeforeUndoStop | InsertFlags.NoAfterUndoStop); }, undefined, listener); }); - this._model.trigger({ auto: false, shy: true }); - this._editor.revealLine(positionNow.lineNumber, ScrollType.Smooth); - this._editor.focus(); + this.model.trigger({ auto: false, shy: true }); + this.editor.revealLine(positionNow.lineNumber, ScrollType.Smooth); + this.editor.focus(); } 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; @@ -415,55 +534,54 @@ 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.model.cancel(); + this.model.clear(); + 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(); } } @@ -480,7 +598,8 @@ export class TriggerSuggestAction extends EditorAction { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.Space, - mac: { primary: KeyMod.WinCtrl | KeyCode.Space }, + 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 } }); @@ -508,11 +627,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); } })); @@ -529,19 +645,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({ key: 'accept.accept', comment: ['{0} will be a keybinding, e.g "Enter to insert"'] }, "{0} to insert") }, + group: 'left', + order: 1, + when: SuggestContext.HasInsertAndReplaceRange.toNegated() }); +MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, { + command: { id: 'acceptSelectedSuggestion', title: nls.localize({ key: 'accept.insert', comment: ['{0} will be a keybinding, e.g "Enter to insert"'] }, "{0} to insert") }, + group: 'left', + order: 1, + when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, ContextKeyExpr.equals('config.editor.suggest.insertMode', 'insert')) +}); +MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, { + command: { id: 'acceptSelectedSuggestion', title: nls.localize({ key: 'accept.replace', comment: ['{0} will be a keybinding, e.g "Enter to replace"'] }, "{0} to replace") }, + group: 'left', + order: 1, + when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, ContextKeyExpr.equals('config.editor.suggest.insertMode', '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, ContextKeyExpr.equals('config.editor.suggest.insertMode', 'insert')), + title: nls.localize({ key: 'accept.replace', comment: ['{0} will be a keybinding, e.g "Enter to replace"'] }, "{0} to replace") + }, { + menuId: suggestWidgetStatusbarMenu, + group: 'left', + order: 2, + when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, ContextKeyExpr.equals('config.editor.suggest.insertMode', 'replace')), + title: nls.localize({ key: 'accept.insert', comment: ['{0} will be a keybinding, e.g "Enter to insert"'] }, "{0} to insert") + }] +})); + // continue to support the old command CommandsRegistry.registerCommandAlias('acceptSelectedSuggestionOnEnter', 'acceptSelectedSuggestion'); @@ -629,7 +781,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({ @@ -659,6 +824,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(), @@ -678,6 +844,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(), @@ -694,6 +861,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(), diff --git a/src/vs/editor/contrib/suggest/suggestMemory.ts b/src/vs/editor/contrib/suggest/suggestMemory.ts index 20f3b4b352df4..b2c98b605e20d 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 a6b85326cfe36..718471864dea0 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -7,7 +7,7 @@ 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'; @@ -21,6 +21,8 @@ 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'; export interface ICancelEvent { readonly retrigger: boolean; @@ -95,7 +97,7 @@ export class SuggestModel implements IDisposable { private readonly _toDispose = new DisposableStore(); private _quickSuggestDelay: number = 10; - private _triggerCharacterListener?: IDisposable; + private readonly _triggerCharacterListener = new DisposableStore(); private readonly _triggerQuickSuggest = new TimeoutTimer(); private _state: State = State.Idle; @@ -115,7 +117,8 @@ export class SuggestModel implements IDisposable { constructor( private readonly _editor: ICodeEditor, - private readonly _editorWorker: IEditorWorkerService + private readonly _editorWorkerService: IEditorWorkerService, + private readonly _clipboardService: IClipboardService ) { this._currentSelection = this._editor.getSelection() || new Selection(1, 1, 1, 1); @@ -141,10 +144,10 @@ export class SuggestModel implements IDisposable { })); let editorIsComposing = false; - this._toDispose.add(this._editor.onCompositionStart(() => { + this._toDispose.add(this._editor.onDidCompositionStart(() => { editorIsComposing = true; })); - this._toDispose.add(this._editor.onCompositionEnd(() => { + this._toDispose.add(this._editor.onDidCompositionEnd(() => { // refilter when composition ends editorIsComposing = false; this._refilterCompletionItems(); @@ -181,8 +184,7 @@ export class SuggestModel implements IDisposable { } private _updateTriggerCharacters(): void { - - dispose(this._triggerCharacterListener); + this._triggerCharacterListener.clear(); if (this._editor.getOption(EditorOption.readOnly) || !this._editor.hasModel() @@ -191,29 +193,49 @@ export class SuggestModel implements IDisposable { return; } - const supportsByTriggerCharacter: { [ch: string]: Set } = Object.create(null); + const supportsByTriggerCharacter = new Map>(); for (const support of CompletionProviderRegistry.all(this._editor.getModel())) { for (const ch of support.triggerCharacters || []) { - let set = supportsByTriggerCharacter[ch]; + let set = supportsByTriggerCharacter.get(ch); if (!set) { - set = supportsByTriggerCharacter[ch] = new Set(); + set = new Set(); set.add(getSnippetSuggestSupport()); + supportsByTriggerCharacter.set(ch, set); } set.add(support); } } - this._triggerCharacterListener = this._editor.onDidType(text => { - const lastChar = text.charAt(text.length - 1); - const supports = supportsByTriggerCharacter[lastChar]; + const checkTriggerCharacter = (text?: string) => { + + if (!text) { + // came here from the compositionEnd-event + const position = this._editor.getPosition()!; + const model = this._editor.getModel()!; + text = model.getLineContent(position.lineNumber).substr(0, position.column - 1); + } + + let lastChar = ''; + if (isLowSurrogate(text.charCodeAt(text.length - 1))) { + if (isHighSurrogate(text.charCodeAt(text.length - 2))) { + lastChar = text.substr(text.length - 2); + } + } else { + lastChar = text.charAt(text.length - 1); + } + + const supports = supportsByTriggerCharacter.get(lastChar); 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; + const items = this._completionModel?.adopt(supports); this.trigger({ auto: true, shy: false, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, items); } - }); + }; + + this._triggerCharacterListener.add(this._editor.onDidType(checkTriggerCharacter)); + this._triggerCharacterListener.add(this._editor.onDidCompositionEnd(checkTriggerCharacter)); } // --- trigger/retrigger/cancel suggest @@ -266,9 +288,7 @@ export class SuggestModel implements IDisposable { ) { // Early exit if nothing needs to be done! // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) - if (this._state !== State.Idle) { - this.cancel(); - } + this.cancel(); return; } @@ -404,9 +424,9 @@ export class SuggestModel implements IDisposable { } let itemKindFilter = SuggestModel._createItemKindFilter(this._editor); - let wordDistance = WordDistance.create(this._editorWorker, this._editor); + let wordDistance = WordDistance.create(this._editorWorkerService, this._editor); - let items = provideSuggestionItems( + let completions = provideSuggestionItems( model, this._editor.getPosition(), new CompletionOptions(snippetSortOrder, itemKindFilter, onlyFrom), @@ -414,9 +434,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; @@ -426,7 +446,13 @@ export class SuggestModel implements IDisposable { return; } + let clipboardText: string | undefined; + if (completions.needsClipboard || isNonEmptyArray(existingItems)) { + clipboardText = await this._clipboardService.readText(); + } + const model = this._editor.getModel(); + let items = completions.items; if (isNonEmptyArray(existingItems)) { const cmpFn = getSuggestionComparator(snippetSortOrder); @@ -440,15 +466,12 @@ 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.dispoables); this._onNewContext(ctx); @@ -493,6 +516,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; } @@ -531,6 +556,12 @@ 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 + this.trigger({ auto: this._context.auto, shy: false }, true); + 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; diff --git a/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts b/src/vs/editor/contrib/suggest/suggestOvertypingCapturer.ts new file mode 100644 index 0000000000000..44eec0ca874dc --- /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) { + 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/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index b3dc600b54b1f..107bee50e3727 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -4,13 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/suggest'; +import 'vs/css!./media/suggestStatusBar'; +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 { 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 { append, $, hide, show, getDomNodePagePosition, addDisposableListener, addStandardDisposableListener, addClasses } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListEvent, IListRenderer, 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'; @@ -18,12 +21,11 @@ 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 { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; -import { Context as SuggestContext, CompletionItem } from './suggest'; +import { Context as SuggestContext, CompletionItem, suggestWidgetStatusbarMenu } from './suggest'; import { CompletionModel } from './completionModel'; -import { alert } from 'vs/base/browser/ui/aria/aria'; 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'; @@ -38,18 +40,39 @@ 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 { flatten, isFalsyOrEmpty } from 'vs/base/common/arrays'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IMenuService } from 'vs/platform/actions/common/actions'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction, IActionViewItemProvider } from 'vs/base/common/actions'; +import { Codicon, registerIcon } from 'vs/base/common/codicons'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; const expandSuggestionDocsByDefault = false; +const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight); + interface ISuggestionTemplateData { root: HTMLElement; + + /** + * Flexbox + * < ------------- left ------------ > < --- right -- > + *